/******************************************************************************
 * Name         : drawvarray.c
 *
 * Copyright    : 2006-2010 by Imagination Technologies Limited.
 *              : All rights reserved. No part of this software, either 
 *              : material or conceptual may be copied or distributed,
 *              : transmitted, transcribed, stored in a retrieval system or
 *              : translated into any human or computer language in any form
 *              : by any means, electronic, mechanical, manual or otherwise,
 *              : or disclosed to third parties without the express written
 *              : permission of Imagination Technologies Limited,
 *              : Home Park Estate, Kings Langley, Hertfordshire,
 *              : WD4 8LZ, U.K.
 *
 * Platform     : ANSI
 *
 * $Log: drawvarray.c $
 *****************************************************************************/

#include "context.h"

#define GLES1_NUM_STATIC_INDICES 1024


/* Whether the primitive eType is natively supported by the hardware */
static const IMG_BOOL primDirectIndex[7] =
{
	IMG_TRUE,  /* GL_POINTS         */
	IMG_TRUE,  /* GL_LINES          */
	IMG_FALSE, /* GL_LINE_LOOP      */
	IMG_FALSE, /* GL_LINE_STRIP     */
	IMG_TRUE,  /* GL_TRIANGLES      */
	IMG_TRUE,  /* GL_TRIANGLE_STRIP */
	IMG_TRUE   /* GL_TRIANGLE_FAN   */
};

/* Number of indices required by the hardware per GL index (see primDirectIndex) */
static const IMG_UINT32 primIndexMult[7] =
{
	1,         /* GL_POINTS         */
	1,         /* GL_LINES          */
	2,         /* GL_LINE_LOOP      */
	2,         /* GL_LINE_STRIP     */
	1,         /* GL_TRIANGLES      */
	1,         /* GL_TRIANGLE_STRIP */
	1,         /* GL_TRIANGLE_FAN   */
};


#if defined(FIX_HW_BRN_23946)
	#define MAX_INDEX_ADDRESS		(8 * 1024 * 1024)
#else
	#define MAX_INDEX_ADDRESS		(16 * 1024 * 1024)
#endif

/* Defined as a percentage of the buffer in bytes */
#define MAX_VBUFFER (gc->apsBuffers[CBUF_TYPE_VERTEX_DATA_BUFFER]->ui32SingleKickLimitInBytes - 4)
#define MAX_IBUFFER	(MIN(gc->apsBuffers[CBUF_TYPE_INDEX_DATA_BUFFER]->ui32SingleKickLimitInBytes - 4, MAX_INDEX_ADDRESS))
#define MAX_INDICES_PER_PRIMBLOCK(indexsize) (MIN((MAX_INDEX_ADDRESS / (indexsize)), ~EURASIA_VDM_IDXCOUNT_CLRMSK))

typedef IMG_VOID (*PFNDrawVArray)(GLES1Context *, GLenum, IMG_UINT32, IMG_UINT32, IMG_UINT32, GLenum, const IMG_VOID *, IMG_UINT32, IMG_UINT32);

#define ALIGN_32BIT_INDICES(pui32Indices) pui32Indices = (IMG_UINT32 *)(((IMG_UINT32)pui32Indices + 0x3) & ~0x3)

/* Implementations of glDrawArrays/glDrawElements in decreasing order of performance.

   1- DrawElementsIndexBO. The indices come from a buffer object.
      1.1- Only supports DrawElements.
      1.2- The primitive type must be supported by the hardware.
      1.3- The indices must not be 8-bit.
      1.4- The vertices must fit in the buffer.
      1.5- The indices must come from an buffer object.

   2- DrawArraysAutoIndices. Writes consecutive 16-bit indices.
      2.1- Only supports DrawArrays.
      2.2- The primitive type must be supported by the hardware.
      2.3- The vertices must fit in the buffer.
      2.4- The (autogenerated 16-bit) indices must fit in the buffer.

   3- DrawElementsDeindex. De-indexes the vertices. Reads 16 or 32-bit indices. Generates 16-bit indices.
      4.1- Only meaningful for DrawElements.
      4.2- The deindexed vertices must fit in the buffer.
      4.3- The autogenerated indices must fit in the buffer.

   4- DrawVertexArray. The input indices, if present, can be 16-bit or 32-bit.
      Supports DrawArrays and DrawElements. Memcopies the indices every time it's possible.

   5- DrawBatchOnIBuffer. Sends indices in batches. XXX: Not supported at the moment.
      6.1- The vertices must fit in the buffer.

   6- DrawBatchOnVBuffer. De-indexes vertices and sends them in batches using 16-bit indices.
      The input indices, if any, can be either 16 or 32-bit.
*/


/***********************************************************************************
 Function Name      : WriteDirect16Indices
 Inputs             : gc
                      eMode      - One of GL_POINTS, GL_LINES, GL_TRIANGLES,
                                  GL_TRIANGLE_STRIP or GL_TRIANGLE_FAN.
                      ui32First - Position of the first index to use
                      ui32Count - Number of indices to use
                      pvIndices - Actual indices or NULL for autogenerated consecutive indices.
 Outputs            : -
 Returns            : -
 Description        : Writes 16-bit indices for point, line, triangles,
 					    triangle-strip and triangle-fan primitives into the
 					    index buffer and makes the call to emit the primitive state.
************************************************************************************/
static IMG_VOID WriteDirect16Indices(GLES1Context *gc, GLenum eMode, IMG_UINT32 ui32First,
									 IMG_UINT32 ui32Count, const IMG_VOID *pvIndices)
{
	const IMG_UINT16 *pui16Indices = (const IMG_UINT16 *)pvIndices;
	IMG_UINT16 * IMG_RESTRICT pui16IndicesOut;
	IMG_UINT32 ui32IndexDWords;

	switch(eMode)
	{
		case GL_POINTS:
		{
			if (ui32Count<1)
			{
				CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER);
				return;
			}

			break;
		}
		case GL_LINES:
		{
			/* Make it multiple of 2 */
			ui32Count = ui32Count - ui32Count%2;

			if (ui32Count<2)
			{
				CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER);
				return;
			}

			break;
		}
		case GL_TRIANGLES:
		{
			/* Make it multiple of 3 and fall through */
			ui32Count = ui32Count - ui32Count%3;
		}
		case GL_TRIANGLE_STRIP:
		case GL_TRIANGLE_FAN:
		{
			if (ui32Count<3)
			{
				CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER);
				return;
			} 

			break;
		}
		default :
		{
			/* Can't handle GL_LINE_STRIP or GL_LINE_LOOP in this function */
			PVR_DPF((PVR_DBG_ERROR,"WriteDirect16Indices: Bad eMode (0x%X) - shouldn't be here", eMode));
			return;
		}
	}

	pui16IndicesOut = (IMG_UINT16 *)gc->pui32IndexData;
	
	GLES1_TIME_START(GLES1_TIMER_INDEX_DATA_GENERATE_COPY);

	if (pui16Indices)
	{
		GLES1MemCopy(pui16IndicesOut, &pui16Indices[ui32First], sizeof(IMG_UINT16)*ui32Count);
	}
	else
	{
		IMG_UINT32 i, j;

		for (i=ui32First, j=0; i < ui32Count + ui32First; ++i,j++)
		{
			pui16IndicesOut[j] = (IMG_UINT16)i;
		}
	}

	GLES1_TIME_STOP(GLES1_TIMER_INDEX_DATA_GENERATE_COPY);

	/* Round up mem usage to nearest 32 bits */
	ui32IndexDWords = (ui32Count + 1) >> 1;

	CBUF_UpdateBufferPos(gc->apsBuffers, ui32IndexDWords, CBUF_TYPE_INDEX_DATA_BUFFER);

	GLES1_INC_COUNT(GLES1_TIMER_INDEX_DATA_COUNT, ui32IndexDWords);

	/* Emit state */
	GLES1EmitState(gc, ui32Count, CBUF_GetBufferDeviceAddress(gc->apsBuffers, gc->pui32IndexData, CBUF_TYPE_INDEX_DATA_BUFFER), 0);
}


/***********************************************************************************
 Function Name      : WriteLineStripOrLoop16Indices
 Inputs             : gc
                      eMode      - GL_LINE_STRIP or GL_LINE_LOOP
                      ui32First - Position of the first index to use
                      ui32Count - Number of indices to use
                      pvIndices - Actual indices or NULL for autogenerated consecutive indices.
 Outputs            : 
 Returns            : 
 Description        : Writes 16-bit indices for line strip/loop primitives into the index buffer
					    and makes the call to emit the primitive state.
************************************************************************************/
static IMG_VOID WriteLineStripOrLoop16Indices(GLES1Context *gc, GLenum eMode, IMG_UINT32 ui32First,
									   IMG_UINT32 ui32Count, const IMG_VOID *pvIndices)
{
	const IMG_UINT16 *pui16Indices = (const IMG_UINT16 *)pvIndices;
	IMG_UINT16 * IMG_RESTRICT pui16IndicesOut;
	IMG_UINT32 i, ui32NumIndices, ui32IndexDWords;
	IMG_UINT16 i0, i1;

	GLES1_ASSERT( (eMode == GL_LINE_STRIP) || (eMode == GL_LINE_LOOP) );
	GLES1_ASSERT( ui32First + ui32Count < 64*1024 );

	if (ui32Count<2)
	{
		CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER);
		return;
	}
	
	pui16IndicesOut = (IMG_UINT16 *)gc->pui32IndexData;

	GLES1_TIME_START(GLES1_TIMER_INDEX_DATA_GENERATE_COPY);

	if (pui16Indices)
	{
		/* Copy the indices with replication to emulate a line strip using line segments */
		for (i=ui32First; i < ui32Count + ui32First - 1; pui16IndicesOut += 2)
		{
			i0 = pui16Indices[i++];
			i1 = pui16Indices[i];

			pui16IndicesOut[0] = i0;
			pui16IndicesOut[1] = i1;
		}

		i0 = pui16Indices[i];
		i1 = pui16Indices[ui32First];
	}
	else
	{
		/* Generate the indices with replication to emulate a line strip using line segments */
		for (i=ui32First; i <  ui32Count + ui32First - 1; pui16IndicesOut += 2, i++)
		{
			i0 = (IMG_UINT16)i;
			i1 = (IMG_UINT16)(i+1);

			pui16IndicesOut[0] = (IMG_UINT16)i0;
			pui16IndicesOut[1] = (IMG_UINT16)i1;
		}

		i0 = (IMG_UINT16)i;
		i1 = (IMG_UINT16)ui32First;
	}

	if((eMode == GL_LINE_LOOP) && (i0 != i1))
	{
		/* Add the connection from the last to the first vertex to close the loop */
		pui16IndicesOut[0] = i0;
		pui16IndicesOut[1] = i1;
		pui16IndicesOut += 2;
	}

	GLES1_TIME_STOP(GLES1_TIMER_INDEX_DATA_GENERATE_COPY);

	ui32NumIndices = (IMG_UINT32)(pui16IndicesOut - (IMG_UINT16 *)gc->pui32IndexData);

	/* Round up to nearest 32 bits */
	ui32IndexDWords = (ui32NumIndices + 1) >> 1;

	CBUF_UpdateBufferPos(gc->apsBuffers, ui32IndexDWords, CBUF_TYPE_INDEX_DATA_BUFFER);

	GLES1_INC_COUNT(GLES1_TIMER_INDEX_DATA_COUNT, ui32IndexDWords);

	/* Emit state */
	GLES1EmitState(gc, ui32NumIndices, CBUF_GetBufferDeviceAddress(gc->apsBuffers, gc->pui32IndexData, CBUF_TYPE_INDEX_DATA_BUFFER), 0);
}




static IMG_VOID (* const WriteIndices[7])(GLES1Context *gc, GLenum mode, IMG_UINT32 ui32First, IMG_UINT32 ui32Count, const IMG_VOID *puiIndices) = 
{
	WriteDirect16Indices,          /* GL_POINTS         */
	WriteDirect16Indices,          /* GL_LINES          */
	WriteLineStripOrLoop16Indices, /* GL_LINE_LOOP      */
	WriteLineStripOrLoop16Indices, /* GL_LINE_STRIP     */
	WriteDirect16Indices,          /* GL_TRIANGLES      */
	WriteDirect16Indices,          /* GL_TRIANGLE_STRIP */
	WriteDirect16Indices,          /* GL_TRIANGLE_FAN   */
};




/***********************************************************************************
 Function Name      : GetNumIndices
 Inputs             : eMode, ui32Count
 Outputs            : Number of indices required
 Returns            : 
 Description        : Returns the number of indices needed to draw the primitive
					    type 'eMode' with original number of vertices 'ui32Count'
************************************************************************************/
static IMG_UINT32 GetNumIndices(GLenum eMode, IMG_UINT32 ui32Count)
{
	GLuint ui32NumIndices;

	switch(eMode)
	{
		case GL_POINTS:
		{
			ui32NumIndices = ui32Count;

			break;
		}
		case GL_LINES:
		{
			ui32Count = ui32Count - ui32Count%2;

			ui32NumIndices = (ui32Count < 2) ? 0 : (ui32Count);

			break;
		}
		case  GL_LINE_LOOP:
		{
			ui32NumIndices = (ui32Count < 2) ? 0 : (2 * ui32Count);

			break;
		}
		case GL_LINE_STRIP:
		{
			ui32NumIndices = (ui32Count < 2) ? 0 : (2 * (ui32Count-1));

			break;
		}
		case GL_TRIANGLES:
		{
			ui32Count = ui32Count - ui32Count%3;

			/* Fall through */
		}
		case GL_TRIANGLE_STRIP:
		case GL_TRIANGLE_FAN:
		{
			ui32NumIndices = (ui32Count < 3) ? 0 : ui32Count;

			break;
		}
		default:
		{
			ui32NumIndices = 0;

			PVR_DPF((PVR_DBG_ERROR,"GetNumIndices: Bad mode (0x%X) - shouldn't be here", eMode));

			break;
		}
	}

	return ui32NumIndices;
}


/***********************************************************************************
 Function Name      : SetupStreamPointers
 Inputs             : gc, ui32First, ui32Count, bAreIndicesZeroBased
 Outputs            : -
 Returns            : -
 Description        : Set up the source, destination and PDS source pointers for vertex attribute copying.
************************************************************************************/
static IMG_VOID SetupStreamPointers(GLES1Context *gc, IMG_UINT32 ui32First,
									IMG_UINT32 ui32Count, IMG_BOOL bAreIndicesZeroBased)
{
	IMG_UINT32 i;
	IMG_UINT8  *pui8VertexData = (IMG_UINT8 *)gc->pvVertexData;
	GLES1VertexArrayObjectMachine *psVAOMachine = &(gc->sVAOMachine);

	for(i = 0; i < psVAOMachine->ui32NumItemsPerVertex; i++)
	{
		GLES1AttribArrayPointerMachine *psAPMachine = psVAOMachine->apsPackedAttrib[i];
		IMG_BOOL bIsBufferObject, bIsCurrentState;
		const IMG_VOID *pvOldPDSSrcAddress;

		/* Assert psAPMachine exists */
		GLES1_ASSERT( psAPMachine );
			
		/* Keep a copy of the old PDS source address */
		pvOldPDSSrcAddress = psAPMachine->pvPDSSrcAddress;

		bIsCurrentState = psAPMachine->bIsCurrentState;
		/*
		 * When determining whether an attribute comes from a buffer object or not,
		 * psVAPState->bIsCurrentState takes precedence over whether psVAPState->psBufObj is null.
		 */
		bIsBufferObject = (!bIsCurrentState && psAPMachine->psState->psBufObj) ? IMG_TRUE : IMG_FALSE;
			
		if(bIsBufferObject)
		{
			/* Neither pui8SrcPointer nor pui8DstPointer need to be set up */
			/* The PDS source 'pointer' is actually an offset. See WritePDSVertexShaderProgram() */
			if(bAreIndicesZeroBased)
			{
				psAPMachine->pvPDSSrcAddress = psAPMachine->pui8CopyPointer + ui32First * psAPMachine->ui32CopyStride;
			}
			else
			{
				psAPMachine->pvPDSSrcAddress = psAPMachine->pui8CopyPointer;
			}
		}
		else
		{
			IMG_UINT32 ui32StreamOffset;

			if(bIsCurrentState)
			{
				/* The source pointer isn't shifted by any offset */
				psAPMachine->pui8SrcPointer = psAPMachine->pui8CopyPointer;
				ui32StreamOffset = psAPMachine->ui32DstSize;
			}
			else
			{
			    /* The following setup is only for the default VAO */
			    if (VAO_IS_ZERO(gc))
				{
					/* The source pointer is shifted by the source offset */
					psAPMachine->pui8SrcPointer = psAPMachine->pui8CopyPointer + ui32First * psAPMachine->ui32CopyStride;
					ui32StreamOffset = psAPMachine->ui32DstSize * ui32Count;

#if defined(NO_UNALIGNED_ACCESS)
					/* Ensure next dest stream is aligned */
					ui32StreamOffset = (ui32StreamOffset + 3) & ~3;
#endif
				}
				else
				{
					ui32StreamOffset = 0;
				}
			}

			/* The destination pointer is always zero-based */
			psAPMachine->pui8DstPointer = pui8VertexData;

			/* The PDS source pointer is shifted by the value of the minimum index */
			if(bAreIndicesZeroBased || bIsCurrentState)
			{
				psAPMachine->pvPDSSrcAddress = (IMG_VOID *)(pui8VertexData);
			}
			else
			{
				psAPMachine->pvPDSSrcAddress = (IMG_VOID *)(pui8VertexData - ui32First * psAPMachine->ui32DstSize);
			}
				
			pui8VertexData += ui32StreamOffset;
		}

		/* If we are changing PDS stream base addresses, we need to set the dirty flag to resend PDS program */
		if(pvOldPDSSrcAddress != psAPMachine->pvPDSSrcAddress)
		{
			gc->ui32DirtyMask |= GLES1_DIRTYFLAG_VAO_ATTRIB_POINTER;
		}
	}
}


/***********************************************************************************
 Function Name      : CopyVArrayData
 Inputs             : gc, ui32First, ui32Count, bAreIndicesZeroBased
 Outputs            : 
 Returns            : 
 Description        : 
************************************************************************************/
static IMG_VOID CopyVArrayData(GLES1Context *gc, IMG_UINT32 ui32First,
							   IMG_UINT32 ui32Count, IMG_BOOL bAreIndicesZeroBased)
{
	IMG_UINT32 i;
	GLES1VertexArrayObjectMachine *psVAOMachine = &(gc->sVAOMachine);
	GLES1AttribArrayPointerMachine *psAPMachine;

	IMG_UINT32 ui32DWordsWritten = (((gc->ui32VertexSize * ui32Count) + gc->ui32VertexRCSize + gc->ui32VertexAlignSize) + 3) >> 2;
#if defined(NO_UNALIGNED_ACCESS)
	IMG_UINT32 ui32Alignment;
#endif

	SetupStreamPointers(gc, ui32First, ui32Count, bAreIndicesZeroBased);

	GLES1_TIME_START(GLES1_TIMER_VERTEX_DATA_COPY);


	for(i = 0; i < psVAOMachine->ui32NumItemsPerVertex; i++)
	{
	    /* Retrieve APMachine from the current VAO */
	    psAPMachine = psVAOMachine->apsPackedAttrib[i];

		/* Assert psAPMachine exists */
		GLES1_ASSERT( psAPMachine );


		if(psAPMachine->bIsCurrentState || !psAPMachine->psState->psBufObj)
		{
			GLES1_ASSERT(psAPMachine->pui8SrcPointer);

#if defined(NO_UNALIGNED_ACCESS)
			ui32Alignment = (((IMG_UINT32) psAPMachine->pui8SrcPointer) & 3) | (((IMG_UINT32) psAPMachine->ui32CopyStride) & 3);

			if(psAPMachine->bIsCurrentState)
			{
				psAPMachine->pfnCopyData[ui32Alignment](psAPMachine->pui8SrcPointer, psAPMachine->pui8DstPointer, psAPMachine->ui32CopyStride, 1);
			}
			else
			{
				psAPMachine->pfnCopyData[ui32Alignment](psAPMachine->pui8SrcPointer, psAPMachine->pui8DstPointer, psAPMachine->ui32CopyStride, ui32Count);
			}
#else
			if(psAPMachine->bIsCurrentState)
			{
				psAPMachine->pfnCopyData(psAPMachine->pui8SrcPointer, psAPMachine->pui8DstPointer, psAPMachine->ui32CopyStride, 1);
			}
			else
			{
				psAPMachine->pfnCopyData(psAPMachine->pui8SrcPointer, psAPMachine->pui8DstPointer, psAPMachine->ui32CopyStride, ui32Count);
			}
#endif
		}
	}

	GLES1_TIME_STOP(GLES1_TIMER_VERTEX_DATA_COPY);

	CBUF_UpdateBufferPos(gc->apsBuffers, ui32DWordsWritten, CBUF_TYPE_VERTEX_DATA_BUFFER);

	GLES1_INC_COUNT(GLES1_TIMER_VERTEX_DATA_COUNT, ui32DWordsWritten);
}


/***********************************************************************************
 Function Name      : CopyVArrayDataDeindex
 Inputs             : gc, ui32First, ui32Count, pvElements
 Outputs            : 
 Returns            : 
 Description        : Copies vertex attributes to the vertex buffer deindexing (gathering) them.
                      If the source of an attribute is a buffer object, the vertices are
                      also copied to the vertex buffer.
************************************************************************************/
static IMG_VOID CopyVArrayDataDeindex(GLES1Context *gc, IMG_UINT32 ui32First,
									  IMG_UINT32 ui32Count, const IMG_VOID *pvElements)
{

	GLES1VertexArrayObjectMachine *psVAOMachine = &(gc->sVAOMachine);
	GLES1AttribArrayPointerMachine *psAPMachine;

	IMG_UINT32 i;
	IMG_UINT32 ui32DWordsWritten = (((gc->ui32VertexSize * ui32Count) + gc->ui32VertexRCSize + gc->ui32VertexAlignSize) + 3) >> 2;
#if defined(NO_UNALIGNED_ACCESS)
	IMG_UINT32 ui32Alignment;
#endif

	GLES1_ASSERT(pvElements);

	SetupStreamPointers(gc, ui32First, ui32Count, IMG_TRUE);

	GLES1_TIME_START(GLES1_TIMER_VERTEX_DATA_COPY);


	for(i=0; i<psVAOMachine->ui32NumItemsPerVertex; i++)
	{
	    /* Retrieve APMachine from the current VAO */
	    psAPMachine = psVAOMachine->apsPackedAttrib[i];

		/* Assert psAPMachine exists */
		GLES1_ASSERT(psAPMachine);
		GLES1_ASSERT(psAPMachine->pui8SrcPointer);
			
		if(psAPMachine->bIsCurrentState)
		{
#if defined(NO_UNALIGNED_ACCESS)
			ui32Alignment = (((IMG_UINT32) psAPMachine->pui8SrcPointer) & 3) | (((IMG_UINT32) psAPMachine->ui32CopyStride) & 3);

			psAPMachine->pfnCopyData[ui32Alignment](psAPMachine->pui8SrcPointer, psAPMachine->pui8DstPointer, psAPMachine->ui32CopyStride, 1);
#else
			psAPMachine->pfnCopyData(psAPMachine->pui8SrcPointer, psAPMachine->pui8DstPointer, psAPMachine->ui32CopyStride, 1);
#endif
		}
		else
		{
			IMG_UINT8 *pui8DstPointer;
			const IMG_UINT8 *pui8SrcPointer, *pui8SrcBasePointer;
			const IMG_UINT16 *pui16Elements = (const IMG_UINT16*)pvElements;
			IMG_UINT32 j;
				
			/* pui8SrcBasePointer points to the base of the vertex data */
			pui8SrcBasePointer = psAPMachine->pui8SrcPointer - ui32First * psAPMachine->ui32Stride;
			pui8DstPointer =  psAPMachine->pui8DstPointer;

			for(j=0; j < ui32Count; j++)
			{
				pui8SrcPointer = pui8SrcBasePointer + pui16Elements[ui32First+j] * psAPMachine->ui32Stride;

#if defined(NO_UNALIGNED_ACCESS)
				ui32Alignment = (((IMG_UINT32) pui8SrcPointer) & 3) | (((IMG_UINT32) psAPMachine->ui32CopyStride) & 3);
				
				psAPMachine->pfnCopyData[ui32Alignment](pui8SrcPointer, pui8DstPointer, psAPMachine->ui32CopyStride, 1);
#else
				psAPMachine->pfnCopyData(pui8SrcPointer, pui8DstPointer, psAPMachine->ui32CopyStride, 1);
#endif
				pui8DstPointer += psAPMachine->ui32DstSize;
			}
		}
	}

	GLES1_TIME_STOP(GLES1_TIMER_VERTEX_DATA_COPY);

	CBUF_UpdateBufferPos(gc->apsBuffers, ui32DWordsWritten, CBUF_TYPE_VERTEX_DATA_BUFFER);

	GLES1_INC_COUNT(GLES1_TIMER_VERTEX_DATA_COUNT, ui32DWordsWritten);
}


/***********************************************************************************
 Function Name      : GetVertexIndexBufferSpace
 Inputs             : gc, uNumIndices, uNumVertices, uSizePerIndex
 Outputs            : 
 Returns            : 
 Description        : check the vertex and index buffer space, flush when necessary 
************************************************************************************/
static IMG_VOID GetVertexIndexBufferSpace(GLES1Context *gc, IMG_UINT32 ui32NumIndices,
											IMG_UINT32 ui32NumVertices, IMG_UINT32 ui32SizePerIndex)
{
	/* Round byte calc up to nearest dword */
	IMG_UINT32 ui32VertexDWords = (((ui32NumVertices * gc->ui32VertexSize) + gc->ui32VertexRCSize + gc->ui32VertexAlignSize) + 3) >> 2;
	IMG_UINT32 ui32IndexDWords = ((ui32NumIndices * ui32SizePerIndex) + 3) >> 2;
	IMG_BOOL bKickTA = IMG_FALSE;

	/* An overflow render could take us out of frame, so check here */
	if(!gc->psRenderSurface->bInFrame)
	{
		IMG_UINT32 ui32NoClears=0;
		IMG_BOOL bSuccess;

		bSuccess = PrepareToDraw(gc, &ui32NoClears, IMG_FALSE);

		if(!bSuccess)
		{
			PVR_DPF((PVR_DBG_ERROR,"GetVertexIndexBufferSpace: Can't prepare to draw"));
		}

		GLES1_ASSERT(bSuccess);
	}

	gc->pvVertexData = (IMG_VOID *) CBUF_GetBufferSpace(gc->apsBuffers, ui32VertexDWords,
												CBUF_TYPE_VERTEX_DATA_BUFFER, IMG_FALSE);

	if(!gc->pvVertexData)
	{
		if(gc->apsBuffers[CBUF_TYPE_VERTEX_DATA_BUFFER]->ui32CommittedPrimOffsetInBytes != 
			gc->apsBuffers[CBUF_TYPE_VERTEX_DATA_BUFFER]->ui32CommittedHWOffsetInBytes)
		{
			bKickTA = IMG_TRUE;

#if defined(DEBUG) || defined(TIMING)
			gc->apsBuffers[CBUF_TYPE_VERTEX_DATA_BUFFER]->ui32KickCount++;
#endif /* defined(DEBUG) || defined(TIMING) */
		}
	}

	/* No need to get index buffer space now if we are about to kick the TA */
	if(!bKickTA)
	{
		gc->pui32IndexData = CBUF_GetBufferSpace(gc->apsBuffers, ui32IndexDWords, CBUF_TYPE_INDEX_DATA_BUFFER, IMG_FALSE);

		if(!gc->pui32IndexData)
		{
			if(gc->apsBuffers[CBUF_TYPE_INDEX_DATA_BUFFER]->ui32CommittedPrimOffsetInBytes != 
				gc->apsBuffers[CBUF_TYPE_INDEX_DATA_BUFFER]->ui32CommittedHWOffsetInBytes)
			{
				bKickTA = IMG_TRUE;

				/* Unlock the vertex buffer space, as we're about to kick */
				CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_VERTEX_DATA_BUFFER);

#if defined(DEBUG) || defined(TIMING)
				gc->apsBuffers[CBUF_TYPE_INDEX_DATA_BUFFER]->ui32KickCount++;
#endif /* defined(DEBUG) || defined(TIMING) */
			}
		}
	}

	if(bKickTA)
	{
		ScheduleTA(gc, gc->psRenderSurface, 0);

		GLES1_ASSERT(gc->psRenderSurface->bInFrame);

		gc->pvVertexData = (IMG_VOID *) CBUF_GetBufferSpace(gc->apsBuffers, ui32VertexDWords,
												CBUF_TYPE_VERTEX_DATA_BUFFER, IMG_FALSE);

		gc->pui32IndexData = CBUF_GetBufferSpace(gc->apsBuffers, ui32IndexDWords, CBUF_TYPE_INDEX_DATA_BUFFER, IMG_FALSE);
	}

	GLES1_ASSERT(gc->pvVertexData);
	GLES1_ASSERT(gc->pui32IndexData);
}


/***********************************************************************************
 Function Name      : CreateSequentialStaticIndices
 Inputs             : gc
 Outputs            : -
 Returns            : Whether static buffer is useable or not
 Description        : Creates a static buffer filled with incremental indices
************************************************************************************/
static IMG_BOOL CreateSequentialStaticIndices(GLES1Context *gc)
{
	IMG_UINT16 i;
	IMG_UINT16 *pui16Index;

	PVRSRVLockMutex(gc->psSharedState->hPrimaryLock);

	if(!gc->psSharedState->psSequentialStaticIndicesMemInfo)
	{
		if(GLES1ALLOCDEVICEMEM(gc->ps3DDevData,
							   gc->psSysContext->hGeneralHeap,
							   PVRSRV_MEM_READ,
							   (GLES1_NUM_STATIC_INDICES*sizeof(IMG_UINT16)),
							   4,
							   &gc->psSharedState->psSequentialStaticIndicesMemInfo) != PVRSRV_OK)
		{
			PVR_DPF((PVR_DBG_ERROR,"CreateSequentialStaticIndices: Can't allocate memory for static index buffer"));

			gc->psSharedState->psSequentialStaticIndicesMemInfo = IMG_NULL;

			PVRSRVUnlockMutex(gc->psSharedState->hPrimaryLock);

			return IMG_FALSE;
		}

#ifdef PDUMP
		gc->psSharedState->bMustDumpSequentialStaticIndices = IMG_TRUE;
#endif

		pui16Index = (IMG_UINT16 *)gc->psSharedState->psSequentialStaticIndicesMemInfo->pvLinAddr;

		for(i=0; i<GLES1_NUM_STATIC_INDICES; i++)
		{
			pui16Index[i] = i;
		}
	}

	PVRSRVUnlockMutex(gc->psSharedState->hPrimaryLock);

	return IMG_TRUE;
}	


/***********************************************************************************
 Function Name      : CreateLineStripStaticIndices
 Inputs             : gc
 Outputs            : -
 Returns            : Whether static buffer is useable or not
 Description        : Creates a static buffer filled with indices for a line strip
************************************************************************************/
static IMG_BOOL CreateLineStripStaticIndices(GLES1Context *gc)
{
	IMG_UINT16 i;
	IMG_UINT16 *pui16Index;

	PVRSRVLockMutex(gc->psSharedState->hPrimaryLock);

	if(!gc->psSharedState->psLineStripStaticIndicesMemInfo)
	{
		if(GLES1ALLOCDEVICEMEM(gc->ps3DDevData,
							   gc->psSysContext->hGeneralHeap,
							   PVRSRV_MEM_READ,
							   (GLES1_NUM_STATIC_INDICES*sizeof(IMG_UINT16)),
							   4,
							   &gc->psSharedState->psLineStripStaticIndicesMemInfo) != PVRSRV_OK)
		{
			PVR_DPF((PVR_DBG_ERROR,"CreateLineStripStaticIndices: Can't allocate memory for static index buffer"));

			gc->psSharedState->psLineStripStaticIndicesMemInfo = IMG_NULL;

			PVRSRVUnlockMutex(gc->psSharedState->hPrimaryLock);

			return IMG_FALSE;
		}

#ifdef PDUMP
		gc->psSharedState->bMustDumpLineStripStaticIndices = IMG_TRUE;
#endif

		pui16Index = (IMG_UINT16 *)gc->psSharedState->psLineStripStaticIndicesMemInfo->pvLinAddr;

		for(i=0; i<GLES1_NUM_STATIC_INDICES/2; i++)
		{
			pui16Index[(2*i)] = i;
			pui16Index[(2*i)+1] = i+1;
		}
	}

	PVRSRVUnlockMutex(gc->psSharedState->hPrimaryLock);

	return IMG_TRUE;
}	


/***********************************************************************************
 Function Name      : GetVArrayBatchRewindCount
 Inputs             : eMode, uBatchSize
 Outputs            : uBatchSize
 Returns            : uRewindCount
 Description        : modify batch size and get the rewind count according to eMode
************************************************************************************/
static IMG_UINT32 GetVArrayBatchRewindCount(GLenum eMode, IMG_UINT32 *ui32BatchSize)
{
	GLuint ui32BatchCount = *ui32BatchSize;
	GLuint ui32RewindCount = 0;

	switch(eMode)
	{
		case GL_TRIANGLES:
		{
			ui32BatchCount -= ui32BatchCount%3;

			break;
		}
		case GL_LINES:
		{
			ui32BatchCount -= ui32BatchCount%2;

			break;
		}
		case GL_TRIANGLE_STRIP:
		{
			ui32BatchCount -= ui32BatchCount%2;

			ui32RewindCount = 2;

			break;
		}
		case GL_LINE_STRIP:
		case GL_LINE_LOOP:
		{
			ui32RewindCount = 1;

			break;
		}
		case GL_TRIANGLE_FAN:
		{
			ui32RewindCount = 2;
		}
		default:
		{
			break;
		}
	}

	*ui32BatchSize = ui32BatchCount;

	return ui32RewindCount;
}


/***********************************************************************************
 Function Name      : DrawVertexArray
 Inputs             : gc
                      eMode - From GL_POINTS to GL_TRIANGLE_FAN
                      ui32First - Position of the first index to read
                      ui32Count - Number of indices to read
                      eType  - Must be GL_UNSIGNED_SHORT
                      elements - Must be NULL or an array of IMG_UINT16 with at least ui32First + ui32Count elements.
                      ui32VertexStart
                      ui32VertexCount
 Outputs            : 
 Returns            : 
 Description        : Implementation of DrawArrays/DrawElements. All vertex
                      and index data must fit in their respective buffers. Writes 16-bit indices
************************************************************************************/
static IMG_VOID DrawVertexArray(GLES1Context *gc, GLenum eMode, IMG_UINT32 ui32First, IMG_UINT32 ui32Count,
								IMG_UINT32 ui32NumIndices, GLenum eType, const IMG_VOID *elements,
								IMG_UINT32 ui32VertexStart, IMG_UINT32 ui32VertexCount)
{
	IMG_BOOL   bIndicesAreZeroBased = (elements==IMG_NULL)?IMG_TRUE:IMG_FALSE;

	GLES1_ASSERT(!elements || eType != GL_UNSIGNED_BYTE);
	PVR_UNREFERENCED_PARAMETER(eType);

	if (bIndicesAreZeroBased && eMode==GL_LINE_STRIP &&
		ui32NumIndices<=GLES1_NUM_STATIC_INDICES && CreateLineStripStaticIndices(gc))
	{
		/* Check the vertex buffer space, flush when necessary */
		GetVertexIndexBufferSpace(gc, 0, ui32VertexCount, sizeof(IMG_UINT16));

		/* Setup stream addresses and copy the vertices attributes if required */
		CopyVArrayData(gc, ui32VertexStart, ui32VertexCount, IMG_TRUE);

		CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER);

		/* Emit state */
		GLES1EmitState(gc, ui32NumIndices, gc->psSharedState->psLineStripStaticIndicesMemInfo->sDevVAddr, 0);
	}
	else
	{
		/* Check the vertex and index buffer space, flush when necessary */
		GetVertexIndexBufferSpace(gc, ui32NumIndices, ui32VertexCount, sizeof(IMG_UINT16));

		/* Setup stream addresses and copy the vertices attributes if required */
		CopyVArrayData(gc, ui32VertexStart, ui32VertexCount, bIndicesAreZeroBased);

		/* Write indices, may be 16 or 32-bit */
		(WriteIndices[eMode])(gc, eMode, bIndicesAreZeroBased?0:ui32First, ui32Count, elements);
	}
}


/***********************************************************************************
 Function Name      : DrawArraysBatchedStaticIndices
 Inputs             : gc, eMode, ui32First, ui32Count, ui32NumIndices, eType, 
 					  elements,	ui32VertexStart, ui32VertexCount
 Outputs            : -
 Returns            : -
 Description        : Send batches of static indices for points, lines, triangles,
					  and triangle strips
************************************************************************************/
static IMG_VOID DrawArraysBatchedStaticIndices(GLES1Context *gc, GLenum eMode, IMG_UINT32 ui32First, IMG_UINT32 ui32Count,
									IMG_UINT32 ui32NumIndices, GLenum eType, const IMG_VOID *elements,
									IMG_UINT32 ui32VertexStart, IMG_UINT32 ui32VertexCount)
{
	IMG_UINT32 ui32IndexOffset, ui32RewindCount, ui32BatchSize;

	/* This function autogenerates the indices */
	GLES1_ASSERT(!elements);
	PVR_UNREFERENCED_PARAMETER(elements);
	PVR_UNREFERENCED_PARAMETER(ui32First);
	PVR_UNREFERENCED_PARAMETER(eType);
	PVR_UNREFERENCED_PARAMETER(ui32Count);
	PVR_UNREFERENCED_PARAMETER(eMode);

	/* Check the vertex buffer space, flush when necessary */
	GetVertexIndexBufferSpace(gc, 0, ui32VertexCount, sizeof(IMG_UINT16));

	/* Setup stream addresses and copy the vertices attributes if required */
	CopyVArrayData(gc, ui32VertexStart, ui32VertexCount, IMG_TRUE);

	CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER);

	ui32IndexOffset = 0;

	ui32RewindCount = GetVArrayBatchRewindCount(eMode, &ui32BatchSize);

	do
	{
		ui32BatchSize = MIN((ui32NumIndices-ui32IndexOffset), GLES1_NUM_STATIC_INDICES);

		ui32BatchSize = GetNumIndices(eMode, ui32BatchSize);

		/* Emit state */
		GLES1EmitState(gc, ui32BatchSize, gc->psSharedState->psSequentialStaticIndicesMemInfo->sDevVAddr, ui32IndexOffset);

		ui32IndexOffset += ui32BatchSize - ui32RewindCount;

	} while(ui32IndexOffset + ui32RewindCount < ui32NumIndices);
}


/***********************************************************************************
 Function Name      : DrawArraysAutoIndices
 Inputs             : gc
                      eMode - From GL_POINTS to GL_TRIANGLE_FAN
                      ui32First - Position of the first index to read
                      ui32Count - Number of indices to read
                      eType  - Ignored.
                      elements - Must be NULL.
                      ui32VertexStart
                      ui32VertexCount
 Outputs            : 
 Returns            : 
 Description        : Auto generate consecutive index data for DrawArrays. Writes 16-bit indices.
************************************************************************************/
static IMG_VOID DrawArraysAutoIndices(GLES1Context *gc, GLenum eMode, IMG_UINT32 ui32First, IMG_UINT32 ui32Count,
									IMG_UINT32 ui32NumIndices, GLenum eType, const IMG_VOID *elements,
									IMG_UINT32 ui32VertexStart, IMG_UINT32 ui32VertexCount)
{
	/* This function autogenerates the indices */
	GLES1_ASSERT(!elements);
	PVR_UNREFERENCED_PARAMETER(elements);
	PVR_UNREFERENCED_PARAMETER(ui32First);
	PVR_UNREFERENCED_PARAMETER(eType);
	PVR_UNREFERENCED_PARAMETER(ui32Count);
	PVR_UNREFERENCED_PARAMETER(eMode);

	/* The type must be natively supported by the hardware */
	GLES1_ASSERT(primDirectIndex[eType]);

	if(ui32NumIndices<=GLES1_NUM_STATIC_INDICES && CreateSequentialStaticIndices(gc))
	{
		/* Check the vertex buffer space, flush when necessary */
		GetVertexIndexBufferSpace(gc, 0, ui32VertexCount, sizeof(IMG_UINT16));

		/* Setup stream addresses and copy the vertices attributes if required */
		CopyVArrayData(gc, ui32VertexStart, ui32VertexCount, IMG_TRUE);

		CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER);

		/* Emit state */
		GLES1EmitState(gc, ui32NumIndices, gc->psSharedState->psSequentialStaticIndicesMemInfo->sDevVAddr, 0);
	}
	else
	{
		IMG_UINT16 *pui16Indices;
		IMG_UINT32 ui32IndexDWords, i;

		/* Check the vertex and index buffer space, flush when necessary */
		GetVertexIndexBufferSpace(gc, ui32NumIndices, ui32VertexCount, sizeof(IMG_UINT16));

		/* Setup stream addresses and copy the vertices attributes if required */
		CopyVArrayData(gc, ui32VertexStart, ui32VertexCount, IMG_TRUE);

		/* Write 16-bit indices */
		pui16Indices = (IMG_UINT16 *)gc->pui32IndexData;

		GLES1_TIME_START(GLES1_TIMER_INDEX_DATA_GENERATE_COPY);

		for(i=0; i < ui32NumIndices; ++i)
		{
			pui16Indices[i] = (IMG_UINT16)i;
		}

		GLES1_TIME_STOP(GLES1_TIMER_INDEX_DATA_GENERATE_COPY);

		/* Round up to nearest 32 bits */
		ui32IndexDWords = (ui32NumIndices + 1) >> 1;

		CBUF_UpdateBufferPos(gc->apsBuffers, ui32IndexDWords, CBUF_TYPE_INDEX_DATA_BUFFER);

		GLES1_INC_COUNT(GLES1_TIMER_INDEX_DATA_COUNT, ui32IndexDWords);

		/* Emit state */
		GLES1EmitState(gc, ui32NumIndices, CBUF_GetBufferDeviceAddress(gc->apsBuffers, gc->pui32IndexData, CBUF_TYPE_INDEX_DATA_BUFFER), 0);
	}
}


/***********************************************************************************
 Function Name      : DrawElementsDeindex
 Inputs             : gc
                      eMode - From GL_POINTS to GL_TRIANGLE_FAN
                      ui32First - Position of the first index to read
                      ui32Count - Number of indices to read
                      eType  - Ignored.
                      elements - Must be NULL.
                      ui32VertexStart
                      ui32VertexCount
 Outputs            : 
 Returns            : 
 Description        : De-indexing DrawElements function. All vertex and index data  
                      must fit in their respective buffers. Writes 16-bit indices.
************************************************************************************/
static IMG_VOID DrawElementsDeindex(GLES1Context *gc, GLenum eMode, IMG_UINT32 ui32First, IMG_UINT32 ui32Count,
									IMG_UINT32 ui32NumIndices, GLenum eType, const IMG_VOID *elements,
									IMG_UINT32 ui32VertexStart, IMG_UINT32 ui32VertexCount)
{

	GLES1_ASSERT(elements);

	PVR_UNREFERENCED_PARAMETER(ui32First);
	PVR_UNREFERENCED_PARAMETER(eType);
	PVR_UNREFERENCED_PARAMETER(ui32VertexStart);
	PVR_UNREFERENCED_PARAMETER(ui32VertexCount);

	/* We should not deindex BOs unless v and i data cannot fit in */
	GLES1_ASSERT((gc->sVAOMachine.ui32ControlWord & ATTRIBARRAY_SOURCE_BUFOBJ) == 0);

	/* Check the vertex and index buffer space, flush when necessary */
	GetVertexIndexBufferSpace(gc, ui32NumIndices, ui32Count, sizeof(IMG_UINT16));

	/* Setup stream addresses and copy the vertices attributes if required */
	CopyVArrayDataDeindex(gc, 0, ui32Count, elements);

	/* Write 16-bit indices */
	(WriteIndices[eMode])(gc, eMode, 0, ui32Count, IMG_NULL);
}

/***********************************************************************************
 Function Name      : DrawElementsIndexBO
 Inputs             : gc
                      eMode - From GL_POINTS to GL_TRIANGLE_FAN
                      ui32First - Position of the first index to read
                      ui32Count - Number of indices to read
                      eType  - One of GL_UNSIGNED_SHORT or GL_UNSIGNED_INT
                      elements - Must be an array of IMG_UINT16/32 with at least ui32First + ui32Count elements.
                      ui32VertexStart
                      ui32VertexCount
 Outputs            : 
 Returns            : 
 Description        : The indices come from an index buffer object. All vertex 
                      data should fit in the vertex buffer.
************************************************************************************/
static IMG_VOID DrawElementsIndexBO(GLES1Context *gc, GLenum eMode, IMG_UINT32 ui32First, IMG_UINT32 ui32UnusedCount,
									IMG_UINT32 ui32NumIndices, GLenum eType, const IMG_VOID *elements,
									IMG_UINT32 ui32VertexStart, IMG_UINT32 ui32VertexCount)
{
    PVRSRV_CLIENT_MEM_INFO *psMemInfo;
	IMG_UINT32 ui32Offset = (IMG_UINT32)GLES1_BUFFER_OFFSET(elements);
	IMG_DEV_VIRTADDR uIndexAddress;
	IMG_UINT32 ui32BatchSize, ui32RewindCount, ui32Count, ui32IndexStart;
	GLES1VertexArrayObjectMachine *psVAOMachine = &(gc->sVAOMachine);
	GLESBufferObject *psIndexBO = psVAOMachine->psBoundElementBuffer;


	PVR_UNREFERENCED_PARAMETER(ui32First);
	PVR_UNREFERENCED_PARAMETER(eType);
	PVR_UNREFERENCED_PARAMETER(ui32UnusedCount);
	PVR_UNREFERENCED_PARAMETER(eMode);


	/* Setup psMemInfo using VAOMachine's bound element bufobj */
	GLES1_ASSERT(psIndexBO);

	psMemInfo = psIndexBO->psMemInfo;

	GLES1_ASSERT(psMemInfo);


	/* Check the vertex and index buffer space, flush when necessary */
	GetVertexIndexBufferSpace(gc, 0, ui32VertexCount, 0);

	/* Setup stream addresses and copy the vertices attributes if required */
	CopyVArrayData(gc, ui32VertexStart, ui32VertexCount, IMG_FALSE);

	uIndexAddress.uiAddr = psMemInfo->sDevVAddr.uiAddr;
	uIndexAddress.uiAddr += (IMG_UINT32)ui32Offset;
	
	if(ui32Offset > psMemInfo->uAllocSize)
	{
		PVR_DPF((PVR_DBG_ERROR,"Index offset %u is larger than index buffer size %u", ui32Offset, psMemInfo->uAllocSize));
	}

	CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER);

	/* 
	 * After the first batch has been drawn, it needs some vertices overlaped for the second batch, 
	 * the following function calculates this rewind count, at the same time, modifying uBatchSize
	 * to a proper figure.
	 */
	ui32BatchSize = MIN(ui32NumIndices, MAX_INDICES_PER_PRIMBLOCK(sizeof(IMG_UINT16)));

	/* check if indices batching is needed */
	if(ui32BatchSize < ui32NumIndices)
	{
		ui32RewindCount = GetVArrayBatchRewindCount(eMode, &ui32BatchSize);
	}
	else
	{
		ui32RewindCount = 0;
	}
	ui32IndexStart = 0;

	do
	{	
		ui32Count = MIN(ui32BatchSize, ui32NumIndices - ui32IndexStart);

		/* Emit state */
		GLES1EmitState(gc, ui32Count, uIndexAddress, 0);
		
		ui32IndexStart += ui32Count - ui32RewindCount;
		uIndexAddress.uiAddr += ((ui32Count - ui32RewindCount) * sizeof(IMG_UINT16));

	} while(ui32IndexStart + ui32RewindCount < ui32NumIndices);
}





/***********************************************************************************
 Function Name      : DrawBatchOnVBuffer
 Inputs             : gc, eMode, ui32First, count, eType, elements, ui32VertexStart, ui32VertexCount
 Outputs            : 
 Returns            : 
 Description        : De-indexes the vertices and writes them out in batches.
                      Generates 16-bit indices.
************************************************************************************/
static IMG_VOID DrawBatchOnVBuffer(GLES1Context *gc, GLenum eMode, IMG_UINT32 ui32First, IMG_UINT32 ui32CountIn,
								   IMG_UINT32 ui32NumIndices, GLenum eType, const IMG_VOID *elements,
								   IMG_UINT32 ui32VertexStart, IMG_UINT32 ui32VertexCount)
{
	IMG_UINT32 i;
	IMG_UINT32 ui32IndexStart = 0;
	IMG_UINT32 ui32RewindCount;
	IMG_UINT32 ui32BatchSize = ui32CountIn;
	const IMG_UINT16 *pui16Indices = (const IMG_UINT16 *)elements;
	GLES1AttribArrayPointerMachine *psAPMachine;
	IMG_BOOL bDeIndex = (elements)?IMG_TRUE : IMG_FALSE, bAttributesWereMangled = IMG_FALSE;
	GLESBufferObject *apsBufObjs[GLES1_MAX_ATTRIBS_ARRAY];
	GLES1VertexArrayObjectMachine *psVAOMachine = &(gc->sVAOMachine);

	PVR_UNREFERENCED_PARAMETER(ui32VertexStart);
	PVR_UNREFERENCED_PARAMETER(ui32VertexCount);
	PVR_UNREFERENCED_PARAMETER(eType);
	PVR_UNREFERENCED_PARAMETER(ui32NumIndices);

	/*
	 * If any of the attributes comes from a buffer object,
	 * we have to reset the stream pointers because we are going to de-index.
	 * This is our way to punish the bad bad app that forced us to go through this code path.
	 */
	if(psVAOMachine->ui32ControlWord & ATTRIBARRAY_SOURCE_BUFOBJ)
	{
		bAttributesWereMangled = IMG_TRUE;
		/*
		 * Make a backup copy of the buffer objects pointers of the attributes.
		 * Set up the attrib pointers to force CopyArrayDataDeIndex to copy the vertex attributes from
		 * the buffer objects into the vertex buffer.
		 */
		for(i = 0; i < psVAOMachine->ui32NumItemsPerVertex; i++)
		{
			/* Get psAPMachine & Assert it exists */
		    psAPMachine = psVAOMachine->apsPackedAttrib[i];
			GLES1_ASSERT( psAPMachine );

			apsBufObjs[i] = psAPMachine->psState->psBufObj;
				
			if(apsBufObjs[i])
			{
				psAPMachine->psState->psBufObj = 0;
			}
		}

		gc->ui32DirtyMask |= GLES1_DIRTYFLAG_VAO_ATTRIB_STREAM;
			
		if(ValidateState(gc)!=GLES1_NO_ERROR)
		{
			PVR_DPF((PVR_DBG_ERROR,"DrawBatchOnVBuffer: ValidateState() failed"));
			
			goto ResetAttribsAndReturn;
		}
			
		for(i = 0; i < psVAOMachine->ui32NumItemsPerVertex; i++)
		{
			/* Get psAPMachine & Assert it exists */
		    psAPMachine = psVAOMachine->apsPackedAttrib[i];
			GLES1_ASSERT( psAPMachine );
			/*
			 * The value of pui8CopyPointer is actually an offset based on the buffer object,
			 * so we need to add it to get a real pointer.
			 */
			if(apsBufObjs[i] && !psAPMachine->bIsCurrentState)
			{
				psAPMachine->pui8CopyPointer += (IMG_UINT32)apsBufObjs[i]->psMemInfo->pvLinAddr;
			}
		}
	}

	/* work out the batch size */
	if(gc->ui32VertexSize)
	{
		ui32BatchSize = MIN(ui32BatchSize, (MAX_VBUFFER - gc->ui32VertexRCSize - gc->ui32VertexAlignSize) / gc->ui32VertexSize);
	}

	    ui32BatchSize = MIN(ui32BatchSize, MAX_IBUFFER / (sizeof(IMG_UINT16)*primIndexMult[eMode]));
	ui32BatchSize = MIN(64*1024, ui32BatchSize); 

	/* 
	 * After the first batch has been drawn, it needs some vertices overlaped for the second batch, 
	 * the following function calculates this rewind count, at the same time, modifying uBatchSize
	 * to a proper figure.
	 */
	if(ui32BatchSize < ui32CountIn)
	{
		ui32RewindCount = GetVArrayBatchRewindCount(eMode, &ui32BatchSize);
	}
	else
	{
		ui32RewindCount = 0;
	}

	switch(eMode)
	{
		case GL_POINTS:          /* Intentional fall-through */
		case GL_TRIANGLES:
		case GL_LINES:
		case GL_TRIANGLE_STRIP:
		case GL_LINE_STRIP:
		case GL_LINE_LOOP:
		{
			IMG_UINT32 ui32Count, ui32BatchIndices;

			do
			{	
				ui32Count = MIN(ui32BatchSize, ui32CountIn - ui32IndexStart);
				ui32BatchIndices = GetNumIndices(eMode, ui32Count);

				if(ui32BatchIndices)
				{
					/*check the vertex and index buffer space, flush when necessary */
					GetVertexIndexBufferSpace(gc, ui32BatchIndices, ui32Count, sizeof(IMG_UINT16));

					if(bDeIndex)
					{
						CopyVArrayDataDeindex(gc, ui32IndexStart + ui32First, ui32Count, elements);
					}
					else
					{
						CopyVArrayData(gc, ui32IndexStart + ui32First, ui32Count, IMG_TRUE);
					}

					if(GL_LINE_LOOP == eMode)
					{
						/*
						* We override the line mode because we don't want to connect the last vertex of each
						* batch to the first vertex of each bach.
						*/
						(WriteIndices[GL_LINE_STRIP])(gc, GL_LINE_STRIP, 0, ui32Count, IMG_NULL);
					}
					else
					{
						(WriteIndices[eMode])(gc, eMode, 0, ui32Count, IMG_NULL);
					}
				}
				
				ui32IndexStart += ui32Count - ui32RewindCount;

				CBUF_UpdateVIBufferCommittedPrimOffsets(gc->apsBuffers, &gc->psRenderSurface->bPrimitivesSinceLastTA, (IMG_VOID *)gc, KickLimit_ScheduleTA);

			} while(ui32IndexStart + ui32RewindCount< ui32CountIn);

			if(GL_LINE_LOOP == eMode)
			{
				/* Connect the last vertex with the first one */
				IMG_UINT16 pui16Tmpindex[2];
				IMG_UINT32 ui32PreviousIndex = (ui32RewindCount == 0) ? ui32IndexStart - 1 : ui32IndexStart;
				
				/* Check the vertex and index buffer space, flush when necessary */
				GetVertexIndexBufferSpace(gc, 2, 2, sizeof(IMG_UINT16));

				if(elements)
				{
					pui16Tmpindex[0] = pui16Indices[ui32PreviousIndex+ui32First];
					pui16Tmpindex[1] = pui16Indices[ui32First];
				}
				else
				{
					pui16Tmpindex[0] = (IMG_UINT16) (ui32PreviousIndex + ui32First);
					pui16Tmpindex[1] = (IMG_UINT16) ui32First;
				}

				CopyVArrayDataDeindex(gc, 0, 2, pui16Tmpindex);

				(WriteIndices[GL_LINE_STRIP])(gc, GL_LINE_STRIP, 0, 2, IMG_NULL);
			}

			break;
		}
		case GL_TRIANGLE_FAN:
		{
			IMG_UINT16 * IMG_RESTRICT pui16Tmpindex;
			IMG_UINT32 j, ui32Count, ui32BatchIndices;

			pui16Tmpindex = GLES1Malloc(gc, ui32BatchSize * sizeof(IMG_UINT16));

			if(!pui16Tmpindex)
			{
				PVR_DPF((PVR_DBG_WARNING,"DrawBatchOnVBuffer: Out of memory for pui32Tmpindex"));

				SetError(gc, GL_OUT_OF_MEMORY);

				goto ResetAttribsAndReturn;
			}

			do
			{
				ui32Count = MIN(ui32BatchSize, ui32CountIn - ui32IndexStart);
				ui32BatchIndices = GetNumIndices(eMode, ui32Count);

				if(ui32BatchIndices)
				{
					/* Check the vertex and index buffer space, flush when necessary */
					GetVertexIndexBufferSpace(gc, ui32Count, ui32Count, sizeof(IMG_UINT16));

					/* Check that we don't overflow pui16Tmpindex */
					GLES1_ASSERT(ui32Count <= ui32BatchSize);
				
					if(elements)
					{
						const IMG_UINT16 *pui16Src = &pui16Indices[ui32IndexStart + ui32First];

						pui16Tmpindex[0] = pui16Indices[ui32First];

						for(j = 1; j < ui32Count; ++j)
						{
							pui16Tmpindex[j] = pui16Src[j];
						}
					}
					else
					{
						/* for DrawArrays */
						pui16Tmpindex[0] = (IMG_UINT16) ui32First;

						for(j = 1; j < ui32Count; ++j)
						{
							pui16Tmpindex[j] = (IMG_UINT16)(ui32IndexStart + ui32First + j);
						}
					}

					CopyVArrayDataDeindex(gc, 0, ui32Count, pui16Tmpindex);

					(WriteIndices[eMode])(gc, eMode, 0, ui32Count, IMG_NULL);
				}

				ui32IndexStart += (ui32Count - ui32RewindCount);

				CBUF_UpdateVIBufferCommittedPrimOffsets(gc->apsBuffers, &gc->psRenderSurface->bPrimitivesSinceLastTA, (IMG_VOID *)gc, KickLimit_ScheduleTA);

			} while(ui32IndexStart + ui32RewindCount < ui32CountIn);

			GLES1Free(IMG_NULL, pui16Tmpindex);

			break;
		}
		default:
		{
			PVR_DPF((PVR_DBG_ERROR,"DrawBatchOnVBuffer: Bad mode (0x%X) - shouldn't be here", eMode));
		}
	}

ResetAttribsAndReturn:

	if(bAttributesWereMangled)
	{
		/*
		 * Restore the pointers to buffer objects and set up the dirty flags to recompute the stream pointers.
		 */
	    for(i = 0; i < psVAOMachine->ui32NumItemsPerVertex; i++)
		{
			psVAOMachine->apsPackedAttrib[i]->psState->psBufObj = apsBufObjs[i];
		}

		gc->ui32DirtyMask |= GLES1_DIRTYFLAG_VAO_ATTRIB_STREAM;
	}
}


/***********************************************************************************
 Function Name      : PickDrawArraysProc
 Inputs             : gc, eMode, ui32Count
 Outputs            : 
 Returns            : 
 Description        : Pick DrawArrays process function
************************************************************************************/
static PFNDrawVArray PickDrawArraysProc(GLES1Context *gc, GLenum eMode, IMG_UINT32 ui32Count)
{
	GLuint ui32NumIndices = ui32Count;
	PFNDrawVArray drawVArray;

	IMG_BOOL bVertexFit = IMG_TRUE;
	IMG_BOOL bIndexFit = IMG_TRUE;
	IMG_BOOL bDirectIndices = primDirectIndex[eMode];

	if((ui32NumIndices * sizeof(IMG_UINT16)) > MAX_IBUFFER || (ui32Count > 64*1024))
	{
		bIndexFit = IMG_FALSE;
	}

	if((ui32Count * gc->ui32VertexSize + gc->ui32VertexRCSize + gc->ui32VertexAlignSize) > MAX_VBUFFER)
	{
		bVertexFit = IMG_FALSE;
	}


	if(bVertexFit && 
	   bDirectIndices && 
	  (eMode!=GL_TRIANGLE_FAN) && 
	  (ui32Count>GLES1_NUM_STATIC_INDICES) && 
	   CreateSequentialStaticIndices(gc))
	{
		return &DrawArraysBatchedStaticIndices;
	}

	if(bVertexFit && 
		bIndexFit && 
	    bDirectIndices)
	{
		drawVArray = DrawArraysAutoIndices;

		return drawVArray;
	}

	/* modified the indices needed, recheck the index buffer */
	
	    ui32NumIndices = GetNumIndices(eMode, ui32Count);

	bIndexFit = IMG_TRUE;

	if((ui32NumIndices * sizeof(IMG_UINT16)) > MAX_IBUFFER) 
	{
		bIndexFit = IMG_FALSE;
	}

	if(bVertexFit && bIndexFit)
	{
		drawVArray = DrawVertexArray;
	}
#if 0
	else if(bVertexFit)
	{
		drawVArray = DrawBatchOnIBuffer;
	}
#endif
	else
	{
		drawVArray = DrawBatchOnVBuffer;
	}

	return drawVArray;
}


/***********************************************************************************
 Function Name      : PickDrawElementsProc
 Inputs             : gc, eMode, eType, ui32Count, ui32VertexCount, ui32MaxIndex
 Outputs            : 
 Returns            : 
 Description        : Pick DrawElement process function
************************************************************************************/
static PFNDrawVArray PickDrawElementsProc(GLES1Context *gc, GLenum eMode, GLenum eType,
										  IMG_UINT32 ui32Count, IMG_UINT32 ui32VertexCount, IMG_UINT32 ui32MaxIndex)
{
	IMG_UINT32 ui32NumIndices = ui32Count;
	PFNDrawVArray drawVArray;

	GLES1VertexArrayObjectMachine *psVAOMachine = &(gc->sVAOMachine);
	GLESBufferObject *psIndexBO = psVAOMachine->psBoundElementBuffer;

	IMG_BOOL bIndexBO,
	         bSourceBO,
	         bIndexFit  = IMG_TRUE,
	         bVertexFit = IMG_TRUE,
			 bDeindexCase = IMG_FALSE;

	/* Setup bIndexBO & bSourceBO */
	bIndexBO   = (psIndexBO) ? IMG_TRUE : IMG_FALSE;
	bSourceBO  = (psVAOMachine->ui32ControlWord & ATTRIBARRAY_SOURCE_BUFOBJ) ? IMG_TRUE : IMG_FALSE;


	/* We cannot use an index buffer object in these cases */
	if (eMode==GL_LINE_STRIP ||
		eMode==GL_LINE_LOOP  ||
		((eMode==GL_TRIANGLE_FAN) && (ui32NumIndices > MAX_INDICES_PER_PRIMBLOCK(sizeof(IMG_UINT16)))) ||
		eType==GL_UNSIGNED_BYTE)
	{
		bIndexBO = IMG_FALSE;
	}

	if(!bIndexBO && !bSourceBO)
	{
		if(ui32VertexCount > 2*ui32Count)
		{
			bDeindexCase = IMG_TRUE;
		}
	}

	if(ui32VertexCount * gc->ui32VertexSize + gc->ui32VertexRCSize  + gc->ui32VertexAlignSize> MAX_VBUFFER)
	{
		if(gc->apsBuffers[CBUF_TYPE_VERTEX_DATA_BUFFER]->ui32BufferLimitInBytes < gc->sAppHints.ui32MaxVertexBufferSize)
		{
			CircularBuffer *psBuffer;
			
			if(ScheduleTA(gc, gc->psRenderSurface, GLES1_SCHEDULE_HW_WAIT_FOR_TA) != IMG_EGL_NO_ERROR)
			{
				PVR_DPF((PVR_DBG_ERROR,"PickDrawElementsProc: ScheduleTA did not work properly"));
			}
			
			psBuffer = CBUF_CreateBuffer(gc->ps3DDevData, 
										CBUF_TYPE_VERTEX_DATA_BUFFER, 
										gc->psSysContext->hGeneralHeap, 
										gc->psSysContext->hSyncInfoHeap, 
										gc->psSysContext->sHWInfo.sMiscInfo.hOSGlobalEvent, 
										MIN(gc->apsBuffers[CBUF_TYPE_VERTEX_DATA_BUFFER]->ui32BufferLimitInBytes * 2, gc->sAppHints.ui32MaxVertexBufferSize),
										gc->psSysContext->hPerProcRef);

			if(psBuffer)
			{
				PVR_DPF((PVR_DBG_WARNING,"PickDrawElementsProc: Resized dynamic vertex buffer. Was %u bytes, now %u bytes", 
					gc->apsBuffers[CBUF_TYPE_VERTEX_DATA_BUFFER]->ui32BufferLimitInBytes, psBuffer->ui32BufferLimitInBytes));

				CBUF_DestroyBuffer(gc->ps3DDevData, gc->apsBuffers[CBUF_TYPE_VERTEX_DATA_BUFFER]);

				gc->apsBuffers[CBUF_TYPE_VERTEX_DATA_BUFFER] = psBuffer;	
	
				/* Point the TA Kick status update to the device memory reserved for status updates in the buffer */
				gc->sKickTA.asTAStatusUpdate[CBUF_TYPE_VERTEX_DATA_BUFFER].hKernelMemInfo = gc->apsBuffers[CBUF_TYPE_VERTEX_DATA_BUFFER]->psStatusUpdateMemInfo->hKernelMemInfo;
				gc->sKickTA.asTAStatusUpdate[CBUF_TYPE_VERTEX_DATA_BUFFER].sCtlStatus.sStatusDevAddr.uiAddr = gc->apsBuffers[CBUF_TYPE_VERTEX_DATA_BUFFER]->psStatusUpdateMemInfo->sDevVAddr.uiAddr;

				/* set the read offset pointer in the buffer */
				gc->apsBuffers[CBUF_TYPE_VERTEX_DATA_BUFFER]->pui32ReadOffset = (IMG_UINT32*)gc->apsBuffers[CBUF_TYPE_VERTEX_DATA_BUFFER]->psStatusUpdateMemInfo->pvLinAddr;


			}
			else
			{
				PVR_DPF((PVR_DBG_ERROR,"PickDrawElementsProc: Failed to create larger dynamic vertex buffer"));
			}
		}


		if(ui32VertexCount * gc->ui32VertexSize + gc->ui32VertexRCSize  + gc->ui32VertexAlignSize> MAX_VBUFFER)
		{
			bVertexFit = IMG_FALSE;
		}
	}


	if(bIndexBO)
	{
		if(bVertexFit) 
		{
			drawVArray = DrawElementsIndexBO;	
			
			return drawVArray;
		}
	}

	/* Recompute the number of indices needed, recheck the index buffer */
		ui32NumIndices = GetNumIndices(eMode, ui32Count);

	if((ui32NumIndices * sizeof(IMG_UINT16) > MAX_IBUFFER) || (ui32MaxIndex > 64 * 1024)) 
	{
		/* Either some indices require more than 16 bits or the index buffer is too small */
		bIndexFit = IMG_FALSE;
	}

	if(bVertexFit && bIndexFit)
	{
		if(bDeindexCase)
		{
			drawVArray = DrawElementsDeindex;
		}
		else
		{
			drawVArray = DrawVertexArray;
		}
	}
	else
	{
		drawVArray = DrawBatchOnVBuffer;
	}

	return drawVArray;
}


/***********************************************************************************
 Function Name      : DetermineMinAndMaxIndices
 Inputs             : gc
                      ui32Count - The number of indices in pvIndices
                      eType - One of GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT or GL_UNSIGNED_INT
                      pvIndices - Array of ui32Count indices of type eType.
 Outputs            : pui32MinIndex, pui32MaxIndex
 Returns            : 
 Description        : Determines the minimum and maximum indices that appear in the array pvIndices.
************************************************************************************/
static IMG_VOID DetermineMinAndMaxIndices(GLES1Context *gc, IMG_UINT32 ui32Count, GLenum eType,
										  const IMG_VOID *pvIndices, IMG_UINT32 *pui32MinIndex, IMG_UINT32 *pui32MaxIndex)
{
	IMG_UINT32 ui32MinIndex, ui32MaxIndex;
	const IMG_VOID *pvTmpIndices;
	GLES1VertexArrayObjectMachine *psVAOMachine = &(gc->sVAOMachine);
	GLESBufferObject *psIndexBO = psVAOMachine->psBoundElementBuffer;


	/* Setup pvTmpIndices using the current VAO's bound element buffer object */
	if (psIndexBO) 
	{
	    GLES1_ASSERT(psIndexBO->psMemInfo);

		/* If we are using an index buffer object, then pvIndices is only an offset to the beginning of the buffer */
		pvTmpIndices = psIndexBO->psMemInfo->pvLinAddr;
		pvTmpIndices = (const IMG_VOID *)((IMG_UINTPTR_T)pvTmpIndices + (IMG_UINT32)GLES1_BUFFER_OFFSET(pvIndices));
	}
	else
	{
		pvTmpIndices = pvIndices;
	}	

	GLES1_ASSERT( ui32Count > 0 );

	/*
		We can probably improve the performance of these loops doing branch
		removal, reading in pairs, loop unrolling, etc.
	*/
	switch(eType) 
	{
		case GL_UNSIGNED_BYTE:
		{
			const IMG_UINT8 *pui8Elements = (const IMG_UINT8 *)pvTmpIndices;
			const IMG_UINT8 *pui8ElementsEnd = pui8Elements + ui32Count;

			/* A single element will be both the max and min */
			ui32MinIndex = *pui8Elements++;
			ui32MaxIndex = ui32MinIndex;

			/* Start from the second element */
			while (pui8Elements < pui8ElementsEnd)
			{
				IMG_UINT8 ui8Indx = *pui8Elements++;

				if(ui8Indx < ui32MinIndex)
				{
					ui32MinIndex = ui8Indx;
				}
				/* An else would reduce comparisons, but the branching reduces performance */
				if(ui8Indx > ui32MaxIndex)
				{
					ui32MaxIndex = ui8Indx;
				}
			}

			break;
		}
		case GL_UNSIGNED_SHORT:
		{
			const IMG_UINT16 *pui16Elements = (const IMG_UINT16 *)pvTmpIndices;
			const IMG_UINT16 *pui16ElementsEnd = pui16Elements + ui32Count;

			/* A single element will be both the max and min */
			ui32MinIndex = *pui16Elements++;
			ui32MaxIndex = ui32MinIndex;

			/* Start from the second element */
			while (pui16Elements < pui16ElementsEnd)
			{
				IMG_UINT16 ui16Indx = *pui16Elements++;

				if(ui16Indx < ui32MinIndex)
				{
					ui32MinIndex = ui16Indx;
				}
				/* An else would reduce comparisons, but the branching reduces performance */
				if(ui16Indx > ui32MaxIndex)
				{
					ui32MaxIndex = ui16Indx;
				}
			}

			break;
		}
		default:
		{
			PVR_DPF((PVR_DBG_ERROR,"DetermineMinAndMaxIndices: Bad type (0x%X) - shouldn't be here", eType));
			return;
		}
	}

	*pui32MinIndex = ui32MinIndex;
	*pui32MaxIndex = ui32MaxIndex;
}


/***********************************************************************************
 Function Name      : TransformIndicesTo16Bits
 Inputs             : gc
                      ui32Count - Number of indices to transform
                      eType - Must be GL_UNSIGNED_BYTE
                      pvIndices - Pointer to the array of indices (of type eType)
 Outputs            : -
 Returns            : An array of ui32Count of IMG_UINT16 indices transformed from pvIndices.
 Description        : 
************************************************************************************/
static IMG_UINT16* TransformIndicesTo16Bits(GLES1Context *gc, IMG_UINT32 ui32Count, GLenum eType, const IMG_VOID *pvIndices)
{
	IMG_UINT32  i;
	IMG_UINT16 *pui16OutIndices = GLES1Malloc(gc, sizeof(IMG_UINT16)*ui32Count);
	GLES1VertexArrayObjectMachine *psVAOMachine = &(gc->sVAOMachine);
	GLESBufferObject *psIndexBO = psVAOMachine->psBoundElementBuffer;


	if(!pui16OutIndices)
	{
		PVR_DPF((PVR_DBG_MESSAGE,"TransformIndicesTo16Bits: Out of memory"));
		SetError(gc, GL_OUT_OF_MEMORY);
		return IMG_NULL;
	}

	/* Setup pvIndices using the current VAO's bound element buffer object */
	if (psIndexBO)
	{
		PVRSRV_CLIENT_MEM_INFO *psMemInfo = psIndexBO->psMemInfo;

		if(GLES1_BUFFER_OFFSET(pvIndices) > (GLintptr)psMemInfo->uAllocSize)
		{
			PVR_DPF((PVR_DBG_ERROR,"Index offset %ld is larger than index buffer size %u",GLES1_BUFFER_OFFSET(pvIndices), psMemInfo->uAllocSize));
		}
		/* The given 'indices' pointer is actually an offset when an index buffer object is bound */
		pvIndices = (const IMG_UINT8*)psMemInfo->pvLinAddr + GLES1_BUFFER_OFFSET(pvIndices);
	}

	/* Currently the only transformation we support is from 8-bit to 16-bit. If any other is needed, add it here */
	if(eType == GL_UNSIGNED_BYTE)
	{
		const IMG_UINT8 *pu8Indices = (const IMG_UINT8*)pvIndices;

		/* Promote from 8 bits to 16 bits */
		for(i = 0; i < ui32Count; ++i)
		{
			pui16OutIndices[i] = pu8Indices[i];
		}
	}
	else
	{
		PVR_DPF((PVR_DBG_MESSAGE,"TransformIndicesTo16Bits: Unsupported index type 0x%X", eType));
		GLES1Free(IMG_NULL, pui16OutIndices);
		pui16OutIndices = IMG_NULL;
	}

	return pui16OutIndices;
}



/***********************************************************************************
 Function Name      : AttachTextureDependency
 Inputs             : gc, psTex
 Outputs            : -
 Returns            : -
 Description        : Attaches source sync dependency on the given texture to kick.
************************************************************************************/
static void AttachTextureDependency(GLES1Context *gc, GLESTexture *psTex)
{
	PVRSRV_CLIENT_MEM_INFO *psMemInfo;
	IMG_UINT32 i;

#if defined(GLES1_EXTENSION_EGL_IMAGE)
	if(psTex->psEGLImageTarget && psTex->psEGLImageTarget->psMemInfo
			&& ((psTex->psEGLImageTarget->ui32Flags & EGLIMAGE_FLAGS_COMPOSITION_SYNC)
					|| (gc->sAppHints.bEnableAppTextureDependency)))
	{
		psMemInfo = psTex->psEGLImageTarget->psMemInfo;
	}
	else
#endif
#if defined(GLES1_EXTENSION_TEXTURE_STREAM)
	if(psTex->psBufferDevice)
	{
		psMemInfo = psTex->psBufferDevice->psBuffer[psTex->ui32BufferOffset].psBufferSurface;
	}
	else
#endif
	{
		return;
	}

	/* Don't add the dependency if we've already seen this sync object */
	for(i = 0; i < gc->psRenderSurface->ui32NumSrcSyncs; i++)
	{
		if(gc->psRenderSurface->apsSrcSurfSyncInfo[i] == psMemInfo->psClientSyncInfo)
		{
			return;
		}
	}

	/* Do not attach the texture if we have run out of slots */
	if(gc->psRenderSurface->ui32NumSrcSyncs == SGX_MAX_SRC_SYNCS)
	{
		PVR_DPF((PVR_DBG_WARNING, "Ran out of sync objects on frame %u", gc->ui32FrameNum));
		return;
	}

	gc->psRenderSurface->apsSrcSurfSyncInfo[gc->psRenderSurface->ui32NumSrcSyncs] = psMemInfo->psClientSyncInfo;
	gc->psRenderSurface->ui32NumSrcSyncs++;
}


/***********************************************************************************
 Function Name      : AttachAllUsedResourcesToCurrentSurface
 Inputs             : gc
 Outputs            : -
 Returns            : -
 Description        : Attaches all resources currently bound to the current surface using the
                      frame resource managers.
************************************************************************************/
IMG_INTERNAL IMG_VOID AttachAllUsedResourcesToCurrentSurface(GLES1Context *gc)
{
	IMG_UINT32 i;
	GLESTexture *psTex;

	/* Attach the textures used in the fragment shader */
	for(i=0; i < gc->ui32NumImageUnitsActive; i++)
	{
		IMG_UINT32 ui32Unit = gc->ui32TexImageUnitsEnabled[i];
		IMG_UINT32 ui32Target = gc->sTexture.aui32CurrentTarget[ui32Unit];
			
		GLES1_ASSERT(ui32Unit < GLES1_MAX_TEXTURE_UNITS);
		GLES1_ASSERT(ui32Target < GLES1_TEXTURE_TARGET_MAX);

		/* PRQA S ??? 1 */ /* ui32Unit has been prevalidated in SetupTextureState */
		/* PRQA S ??? 1 */ /* ui32Target has been prevalidated in SetupTextureState */
		psTex = gc->sTexture.apsBoundTexture[ui32Unit][ui32Target];

		GLES1_ASSERT(psTex);

		if(psTex->bResidence)
		{
			AttachTextureDependency(gc, psTex);

			/* Attach this texture to the current frame of the current render surface */
			KRM_Attach(&gc->psSharedState->psTextureManager->sKRM, gc->psRenderSurface, 
					   &gc->psRenderSurface->sRenderStatusUpdate, &psTex->sResource);
		}
	}

	/* Attach USSE fragment variants (set up by SetupUSEFragmentShader) */
	KRM_Attach(&gc->psSharedState->sUSEShaderVariantKRM, gc->psRenderSurface, 
			   &gc->psRenderSurface->sRenderStatusUpdate, &gc->sProgram.psCurrentFragmentVariant->sResource);
}


/***********************************************************************************
 Function Name      : AttachAllUsedBOsAndVAOToCurrentKick
 Inputs             : gc
 Outputs            : -
 Returns            : -
 Description        : Sets up status writebacks for all Buffer Objects 
                      and the VAO used in current kick.
************************************************************************************/
IMG_INTERNAL IMG_VOID AttachAllUsedBOsAndVAOToCurrentKick(GLES1Context *gc)
{

	GLES1VertexArrayObjectMachine *psVAOMachine = &(gc->sVAOMachine);
	GLES1VertexArrayObject *psVAO = gc->sVAOMachine.psActiveVAO;

	GLES1_ASSERT(VAO(gc));


	/* Add in the VAO if it possesses MemInfo for PDS program */
	if (psVAO->psMemInfo)
	{
		KRM_Attach(&gc->sVAOKRM, gc, &gc->sKRMTAStatusUpdate, &psVAO->sResource);
	}

	/* Add in any vertex buffer objects */
	if(psVAOMachine->ui32ControlWord & ATTRIBARRAY_SOURCE_BUFOBJ)
	{
		IMG_UINT32 i;

		for(i=0; i<psVAOMachine->ui32NumItemsPerVertex; i++)
		{
			GLES1AttribArrayPointerMachine *psAPMachine;
			GLESBufferObject *psBufObj;

			psAPMachine = psVAOMachine->apsPackedAttrib[i];
			
			/* Assert psAPMachine exists */
			GLES1_ASSERT(psAPMachine);

			psBufObj = psAPMachine->psState->psBufObj;
			
			if(psBufObj && !psAPMachine->bIsCurrentState)
			{
				KRM_Attach(&gc->psSharedState->sBufferObjectKRM, gc, &gc->sKRMTAStatusUpdate, &psBufObj->sResource);
			}
		}
	}

	/* Add in any index buffer objects */
	if (psVAOMachine->psBoundElementBuffer)
	{
	    GLESBufferObject *psBufObj = psVAOMachine->psBoundElementBuffer;

		KRM_Attach(&gc->psSharedState->sBufferObjectKRM, gc, &gc->sKRMTAStatusUpdate, &psBufObj->sResource);
	}

}


/***********************************************************************************
 Function Name      : HandlePrimitiveTypeChange
 Inputs             : gc, mode
 Outputs            : -
 Returns            : -
 Description        : Set up the current GLES primitive type and set relevant dirty
					  flags for any primitive type change
************************************************************************************/
static void HandlePrimitiveTypeChange(GLES1Context *gc, GLenum mode)
{
	/*
	** Disable depth bias for lines/points
	*/

	switch(mode)
	{
		case GL_POINTS:
		case GL_LINES:
		case GL_LINE_STRIP:
		case GL_LINE_LOOP:
		{
			/* Need to disable offset if previous primitive WAS a polygon */
			if ((gc->sPrim.eCurrentPrimitiveType>=GLES1_PRIMTYPE_TRIANGLE) &&
			    (gc->sPrim.eCurrentPrimitiveType<=GLES1_PRIMTYPE_TRIANGLE_FAN))
		    {
				if (gc->ui32RasterEnables & GLES1_RS_POLYOFFSET_ENABLE)
				{					
					gc->ui32DirtyMask |= GLES1_DIRTYFLAG_RENDERSTATE;
				}
				/* For points and lines disable two sided lighting and generate new shaders */
				if (gc->sState.sLight.sModel.bTwoSided && ((gc->ui32TnLEnables & GLES1_TL_LIGHTING_ENABLE) != 0))
				{
					gc->ui32DirtyMask |= GLES1_DIRTYFLAG_VERTEX_PROGRAM | GLES1_DIRTYFLAG_FRAGMENT_PROGRAM | GLES1_DIRTYFLAG_RENDERSTATE;
				}
		    }

			break;
		}
		default:
		{
			/* Need to enable offset if previous primitive WASN'T a polygon */
			if ((gc->sPrim.eCurrentPrimitiveType<GLES1_PRIMTYPE_TRIANGLE) ||
			    (gc->sPrim.eCurrentPrimitiveType>GLES1_PRIMTYPE_TRIANGLE_FAN))
			{
				if (gc->ui32RasterEnables & GLES1_RS_POLYOFFSET_ENABLE)
				{
					gc->ui32DirtyMask |= GLES1_DIRTYFLAG_RENDERSTATE;
				}
				/* After points and lines enabke two sided lighting enable two sided lighting and generate new shaders */					
				if (gc->sState.sLight.sModel.bTwoSided && ((gc->ui32TnLEnables & GLES1_TL_LIGHTING_ENABLE) != 0))
				{
					gc->ui32DirtyMask |= GLES1_DIRTYFLAG_VERTEX_PROGRAM | GLES1_DIRTYFLAG_FRAGMENT_PROGRAM | GLES1_DIRTYFLAG_RENDERSTATE;
				}					
			}

			break;
		}
	}

	/* Need to restore the attrib streams and switch on viewport transform if we've been using DrawTexture */
	if(gc->sPrim.eCurrentPrimitiveType==GLES1_PRIMTYPE_DRAWTEXTURE)
	{
        gc->ui32DirtyMask |= GLES1_DIRTYFLAG_VAO_ATTRIB_STREAM | GLES1_DIRTYFLAG_VERTEX_PROGRAM;
		gc->ui32EmitMask |=	GLES1_EMITSTATE_MTE_STATE_CONTROL;
	}

#if defined(FIX_HW_BRN_31728)
	if((mode==GL_LINES) || (mode==GL_LINE_STRIP) || (mode==GL_LINE_LOOP))
	{
		if(!((gc->sPrim.eCurrentPrimitiveType==GLES1_PRIMTYPE_LINE)		 ||
		     (gc->sPrim.eCurrentPrimitiveType==GLES1_PRIMTYPE_LINE_LOOP) ||
		     (gc->sPrim.eCurrentPrimitiveType==GLES1_PRIMTYPE_LINE_STRIP)))
		{
            gc->ui32DirtyMask |= (GLES1_DIRTYFLAG_RENDERSTATE | GLES1_DIRTYFLAG_FRAGMENT_PROGRAM);
		}
	}
	else
	{
		if(((gc->sPrim.eCurrentPrimitiveType==GLES1_PRIMTYPE_LINE)		||
		    (gc->sPrim.eCurrentPrimitiveType==GLES1_PRIMTYPE_LINE_LOOP) ||
		    (gc->sPrim.eCurrentPrimitiveType==GLES1_PRIMTYPE_LINE_STRIP)))
		{
            gc->ui32DirtyMask |= (GLES1_DIRTYFLAG_RENDERSTATE | GLES1_DIRTYFLAG_FRAGMENT_PROGRAM);
		}
	}
#endif /* defined(FIX_HW_BRN_31728) */


 	/*
	** Points/sprites need their size coming from an array or a constant
	*/
    if(mode == GL_POINTS)
    {
		/* Changing from a non-point primitive to a point primitive? */
        if(!((gc->sPrim.eCurrentPrimitiveType==GLES1_PRIMTYPE_POINT) ||
             (gc->sPrim.eCurrentPrimitiveType==GLES1_PRIMTYPE_SPRITE)))
        {
            gc->ui32DirtyMask |= GLES1_DIRTYFLAG_VAO_ATTRIB_STREAM | GLES1_DIRTYFLAG_VERTPROG_CONSTANTS;

#if defined(FIX_HW_BRN_29546)
            gc->ui32DirtyMask |= (GLES1_DIRTYFLAG_RENDERSTATE | GLES1_DIRTYFLAG_FRAGMENT_PROGRAM);
#endif /* defined(FIX_HW_BRN_29546) */
        }

		/* Change a point to a sprite */
		if(gc->ui32TnLEnables & GLES1_TL_POINTSPRITE_ENABLE)
		{
			gc->sPrim.eCurrentPrimitiveType = GLES1_PRIMTYPE_SPRITE;
		}
		else
		{
			gc->sPrim.eCurrentPrimitiveType = GLES1_PRIMTYPE_POINT;
		}
    }
    else
    {
		/* Changing from a point primitive to a non-point primitive? */
        if((gc->sPrim.eCurrentPrimitiveType==GLES1_PRIMTYPE_POINT) ||
           (gc->sPrim.eCurrentPrimitiveType==GLES1_PRIMTYPE_SPRITE))
        {
            gc->ui32DirtyMask |= GLES1_DIRTYFLAG_VAO_ATTRIB_STREAM | GLES1_DIRTYFLAG_VERTPROG_CONSTANTS;

#if defined(FIX_HW_BRN_29546)
            gc->ui32DirtyMask |= (GLES1_DIRTYFLAG_RENDERSTATE | GLES1_DIRTYFLAG_FRAGMENT_PROGRAM);
#endif /* defined(FIX_HW_BRN_29546) */
        }

		switch(mode)
		{
			case GL_LINES:
			{
				gc->sPrim.eCurrentPrimitiveType = GLES1_PRIMTYPE_LINE;
				break;
			}
			case GL_LINE_LOOP:
			{
				gc->sPrim.eCurrentPrimitiveType = GLES1_PRIMTYPE_LINE_LOOP;
				break;
			}
			case GL_LINE_STRIP:
			{
				gc->sPrim.eCurrentPrimitiveType = GLES1_PRIMTYPE_LINE_STRIP;
				break;
			}
			case GL_TRIANGLES:
			{
				gc->sPrim.eCurrentPrimitiveType = GLES1_PRIMTYPE_TRIANGLE;
				break;
			}
			case GL_TRIANGLE_STRIP:
			{
				gc->sPrim.eCurrentPrimitiveType = GLES1_PRIMTYPE_TRIANGLE_STRIP;
				break;
			}
			case GL_TRIANGLE_FAN:
			{
				gc->sPrim.eCurrentPrimitiveType = GLES1_PRIMTYPE_TRIANGLE_FAN;
				break;
			}
			case 7:
			{
				gc->sPrim.eCurrentPrimitiveType = GLES1_PRIMTYPE_SPRITE;
				break;
			}
			case 8:
			{
				gc->sPrim.eCurrentPrimitiveType = GLES1_PRIMTYPE_DRAWTEXTURE;
				break;
			}
		}
    }

}	


/***********************************************************************************
 Function Name      : glDrawArrays
 Inputs             : mode, first, count
 Outputs            : -
 Returns            : -
 Description        : ENTRYPOINT: Draws primitives of type mode using attrib arrays. 
					  Will validate as necessary, then send control, state and attrib 
					  data to HW.
************************************************************************************/
GL_API void GL_APIENTRY glDrawArrays(GLenum mode, GLint first, GLsizei count)
{
	PFNDrawVArray pfnDrawArrays;
	IMG_UINT32 ui32NumIndices;
	IMG_UINT32 ui32NoClears = 0;
	GLES1VertexArrayObjectMachine *psVAOMachine;
	GLES1VertexArrayObject *psVAO;

	
	__GLES1_GET_CONTEXT();

	PVR_DPF((PVR_DBG_CALLTRACE,"glDrawArrays"));

	GLES1_TIME_START(GLES1_TIMES_glDrawArrays);
	GLES1_TIME_START(GLES1_TIMER_ARRAY_POINTS_TIME+(mode & GLES1_TIMER_ARRAY_MODE_MASK));

	/* Setup VAOMachine and VAO */
	psVAOMachine = &(gc->sVAOMachine);
	psVAO = psVAOMachine->psActiveVAO;

	/* Assert VAO is not NULL */
	GLES1_ASSERT(psVAOMachine);
	GLES1_ASSERT(psVAO);


	/* check that first and count are positive */
	if(first < 0 || count < 0) 
	{
		SetError(gc, GL_INVALID_VALUE);
	
		GLES1_TIME_STOP(GLES1_TIMER_ARRAY_POINTS_TIME+(mode & GLES1_TIMER_ARRAY_MODE_MASK));
		GLES1_TIME_STOP(GLES1_TIMES_glDrawArrays);

		return;
	}

	if(mode > GL_TRIANGLE_FAN) 
	{
		SetError(gc, GL_INVALID_ENUM);

		GLES1_TIME_STOP(GLES1_TIMER_ARRAY_POINTS_TIME+(mode & GLES1_TIMER_ARRAY_MODE_MASK));
		GLES1_TIME_STOP(GLES1_TIMES_glDrawArrays);

		return;
	}

	ui32NumIndices = GetNumIndices(mode, (IMG_UINT32)count);

	if(count == 0 || ui32NumIndices == 0)
	{
		GLES1_TIME_STOP(GLES1_TIMER_ARRAY_POINTS_TIME+mode);
		GLES1_TIME_STOP(GLES1_TIMES_glDrawArrays);

		return;
	}


	/* All the checking referring to psVAO */
	if((psVAO->ui32CurrentArrayEnables & VARRAY_PROVOKE) == 0)
	{
		GLES1_TIME_STOP(GLES1_TIMER_ARRAY_POINTS_TIME+mode);
		GLES1_TIME_STOP(GLES1_TIMES_glDrawArrays);
		
		return;
	}

	/* Matrix palette must have valid pointers for index and weights */
	if(gc->ui32TnLEnables & GLES1_TL_MATRIXPALETTE_ENABLE)
	  {
		if((psVAO->ui32CurrentArrayEnables & (VARRAY_WEIGHTARRAY_ENABLE | VARRAY_MATRIXINDEX_ENABLE)) !=
		                                     (VARRAY_WEIGHTARRAY_ENABLE | VARRAY_MATRIXINDEX_ENABLE))
		{
			GLES1_TIME_STOP(GLES1_TIMER_ARRAY_POINTS_TIME+mode);
			GLES1_TIME_STOP(GLES1_TIMES_glDrawArrays);

			return;
		}
	}

	if(GetFrameBufferCompleteness(gc) != GL_FRAMEBUFFER_COMPLETE_OES)
	{
		SetError(gc, GL_INVALID_FRAMEBUFFER_OPERATION_OES);

		GLES1_TIME_STOP(GLES1_TIMER_ARRAY_POINTS_TIME+mode);
		GLES1_TIME_STOP(GLES1_TIMES_glDrawArrays);

		return;
	}	

	if ((gc->sState.sPolygon.eCullMode==GL_FRONT_AND_BACK) && ((gc->ui32TnLEnables & GLES1_TL_CULLFACE_ENABLE) != 0) &&
	   (mode!=GL_LINES) && (mode!=GL_LINE_LOOP) && (mode!=GL_LINE_STRIP) && (mode!=GL_POINTS))
	{
		/* Return if there's nothing to draw due to front_and_back culling */
		GLES1_TIME_STOP(GLES1_TIMER_ARRAY_POINTS_TIME+mode);
		GLES1_TIME_STOP(GLES1_TIMES_glDrawArrays);

		return;
	}

	HandlePrimitiveTypeChange(gc, mode);

	if(!PrepareToDraw(gc, &ui32NoClears, IMG_TRUE))
	{
		PVR_DPF((PVR_DBG_ERROR,"glDrawArrays: Can't prepare to draw"));

		GLES1_TIME_STOP(GLES1_TIMER_ARRAY_POINTS_TIME+mode);
		GLES1_TIME_STOP(GLES1_TIMES_glDrawArrays);

		return;
	}

	if(gc->ui32DirtyMask || gc->sVAOMachine.psActiveVAO->ui32DirtyMask)
	{
		if(ValidateState(gc)!=GLES1_NO_ERROR)
		{
			PVR_DPF((PVR_DBG_ERROR,"glDrawArrays: ValidateState() failed"));
			
			PVRSRVUnlockMutex(gc->psRenderSurface->hMutex);
			
			GLES1_TIME_STOP(GLES1_TIMER_ARRAY_POINTS_TIME+mode);
			GLES1_TIME_STOP(GLES1_TIMES_glDrawArrays);

			return;
		}
	}

	/* Check whether any buffer object has undefined memory. 
	   This could cause visual corruption or lockup, so return without drawing. 
	   An OUT_OF_MEMORY error will have been raised at the point of allocation failure.
	 */
	if (psVAOMachine->ui32ControlWord & ATTRIBARRAY_BAD_BUFOBJ)
	{
		PVRSRVUnlockMutex(gc->psRenderSurface->hMutex);

	    GLES1_TIME_STOP(GLES1_TIMER_ARRAY_POINTS_TIME+mode);
		GLES1_TIME_STOP(GLES1_TIMES_glDrawArrays);

		return;
	}

#if defined(GLES1_EXTENSION_MAP_BUFFER)
	/* Check whether any buffer object is mapped */
	if(psVAOMachine->ui32ControlWord & ATTRIBARRAY_MAP_BUFOBJ)
	{
		SetError(gc, GL_INVALID_OPERATION);

		PVRSRVUnlockMutex(gc->psRenderSurface->hMutex);

		GLES1_TIME_STOP(GLES1_TIMER_ARRAY_POINTS_TIME+mode);
		GLES1_TIME_STOP(GLES1_TIMES_glDrawArrays);
		
		return;
	}
#endif /* defined(GLES1_EXTENSION_MAP_BUFFER) */

	/* Attach all used resources to the current surface */
	AttachAllUsedResourcesToCurrentSurface(gc);

	pfnDrawArrays = PickDrawArraysProc(gc, mode, (IMG_UINT32)count);

	(*pfnDrawArrays)(gc, mode, (IMG_UINT32)first, (IMG_UINT32)count, ui32NumIndices, 0, IMG_NULL, (IMG_UINT32)first, (IMG_UINT32)count);

	/*
		Update vertex and index buffers committed primitive offset
	*/
	CBUF_UpdateVIBufferCommittedPrimOffsets(gc->apsBuffers, &gc->psRenderSurface->bPrimitivesSinceLastTA, (IMG_VOID *)gc, KickLimit_ScheduleTA);

	PVRSRVUnlockMutex(gc->psRenderSurface->hMutex);

	GLES1_TIME_STOP(GLES1_TIMER_ARRAY_POINTS_TIME+mode);
	GLES1_TIME_STOP(GLES1_TIMES_glDrawArrays);

	GLES1_PROFILE_INCREMENT_DRAWARRAYS_CALLCOUNT(mode);
	GLES1_PROFILE_INCREMENT_DRAWARRAYS_VERTEXCOUNT(mode, (IMG_UINT32)count);

	GLES1_PROFILE_ADD_STATE_METRIC;
}


/***********************************************************************************
 Function Name      : glDrawElements
 Inputs             : mode, count, type indices
 Outputs            : -
 Returns            : -
 Description        : ENTRYPOINT: Draws indexed primitives of type mode using attrib 
					  arrays and an index list. Will validate as necessary, then send 
					  control, state and attrib data to HW.
************************************************************************************/
GL_API void GL_APIENTRY glDrawElements(GLenum mode, GLsizei count, GLenum type, const void *indices)
{
	IMG_UINT32 ui32MinIndex=0xFFFFFFFF, ui32MaxIndex=0,ui32NumIndices;
	IMG_UINT32 ui32VertexStart, ui32VertexCount;
	PFNDrawVArray pfnDrawElements;
	const IMG_UINT16 *pui16Elements = (const IMG_UINT16 *)indices;
	IMG_UINT16 *pui16NewElements = IMG_NULL;
	IMG_BOOL    bIndicesWerePromoted = IMG_FALSE;
	IMG_UINT32  ui32NoClears = 0;
	GLES1VertexArrayObjectMachine *psVAOMachine;
	GLES1VertexArrayObject *psVAO;


	__GLES1_GET_CONTEXT();

	PVR_DPF((PVR_DBG_CALLTRACE,"glDrawElements"));

	GLES1_TIME_START(GLES1_TIMES_glDrawElements);
	GLES1_TIME_START(GLES1_TIMER_ELEMENT_POINTS_TIME+(mode & GLES1_TIMER_ARRAY_MODE_MASK));


	/* Setup VAOMachine and VAO */
	psVAOMachine = &(gc->sVAOMachine);
	psVAO = psVAOMachine->psActiveVAO;

	/* Assert VAO is not NULL */
	GLES1_ASSERT(psVAOMachine);
	GLES1_ASSERT(psVAO);


	/* check that count is positive */
	if(count < 0) 
	{
		SetError(gc, GL_INVALID_VALUE);

		GLES1_TIME_STOP(GLES1_TIMER_ELEMENT_POINTS_TIME+(mode & GLES1_TIMER_ARRAY_MODE_MASK));
		GLES1_TIME_STOP(GLES1_TIMES_glDrawElements);

		return;
	}

	if(mode > GL_TRIANGLE_FAN) 
	{
bad_enum:
		SetError(gc, GL_INVALID_ENUM);

		GLES1_TIME_STOP(GLES1_TIMER_ELEMENT_POINTS_TIME+(mode & GLES1_TIMER_ARRAY_MODE_MASK));
		GLES1_TIME_STOP(GLES1_TIMES_glDrawElements);

		return;
	}

	switch(type) 
	{
		case GL_UNSIGNED_BYTE:
		case GL_UNSIGNED_SHORT:
		{
			break;
		}
		default:
		{
			goto bad_enum;
		}
	}

	ui32NumIndices = GetNumIndices(mode, (IMG_UINT32)count);

	if(count == 0 || ui32NumIndices == 0)
	{
		GLES1_TIME_STOP(GLES1_TIMER_ELEMENT_POINTS_TIME+mode);
		GLES1_TIME_STOP(GLES1_TIMES_glDrawElements);

		return;
	}

	/* All the checking referring to psVAO */
	if((psVAO->ui32CurrentArrayEnables & VARRAY_PROVOKE) == 0)
	{
		GLES1_TIME_STOP(GLES1_TIMER_ELEMENT_POINTS_TIME+mode);
		GLES1_TIME_STOP(GLES1_TIMES_glDrawElements);
		
		return;
	}

	/* Matrix palette must have valid pointers for index and weights */
	if(gc->ui32TnLEnables & GLES1_TL_MATRIXPALETTE_ENABLE)
	{
		if((psVAO->ui32CurrentArrayEnables & (VARRAY_WEIGHTARRAY_ENABLE | VARRAY_MATRIXINDEX_ENABLE)) != 
									         (VARRAY_WEIGHTARRAY_ENABLE | VARRAY_MATRIXINDEX_ENABLE))
		{
			GLES1_TIME_STOP(GLES1_TIMER_ELEMENT_POINTS_TIME+mode);
			GLES1_TIME_STOP(GLES1_TIMES_glDrawElements);

			return;
		}
	}


	if(GetFrameBufferCompleteness(gc) != GL_FRAMEBUFFER_COMPLETE_OES)
	{
		SetError(gc, GL_INVALID_FRAMEBUFFER_OPERATION_OES);

		GLES1_TIME_STOP(GLES1_TIMER_ELEMENT_POINTS_TIME+mode);
		GLES1_TIME_STOP(GLES1_TIMES_glDrawElements);

		return;
	}

	if ((gc->sState.sPolygon.eCullMode==GL_FRONT_AND_BACK) && ((gc->ui32TnLEnables & GLES1_TL_CULLFACE_ENABLE) !=0) && 
	   (mode!=GL_LINES) && (mode!=GL_LINE_LOOP) && (mode!=GL_LINE_STRIP) && (mode!=GL_POINTS))
	{
		/* Return if there's nothing to draw due to front_and_back culling */

		GLES1_TIME_STOP(GLES1_TIMER_ELEMENT_POINTS_TIME+mode);
		GLES1_TIME_STOP(GLES1_TIMES_glDrawElements);

		return;
	}

	HandlePrimitiveTypeChange(gc, mode);

	if(!PrepareToDraw(gc, &ui32NoClears, IMG_TRUE))
	{
		PVR_DPF((PVR_DBG_ERROR,"glDrawElements: Can't prepare to draw"));

		GLES1_TIME_STOP(GLES1_TIMER_ELEMENT_POINTS_TIME+mode);
		GLES1_TIME_STOP(GLES1_TIMES_glDrawElements);

		return;
	}

	if(gc->ui32DirtyMask || gc->sVAOMachine.psActiveVAO->ui32DirtyMask)
	{
		if(ValidateState(gc)!=GLES1_NO_ERROR)
		{
			PVRSRVUnlockMutex(gc->psRenderSurface->hMutex);

			PVR_DPF((PVR_DBG_ERROR,"glDrawElements: ValidateState() failed"));
			
			GLES1_TIME_STOP(GLES1_TIMER_ELEMENT_POINTS_TIME+mode);
			GLES1_TIME_STOP(GLES1_TIMES_glDrawElements);

			return;
		}
	}

	/* Check whether any buffer object has undefined memory. 
	   This could cause visual corruption or lockup, so return without drawing. 
	   An OUT_OF_MEMORY error will have been raised at the point of allocation failure.
	*/
	if(psVAOMachine->ui32ControlWord & ATTRIBARRAY_BAD_BUFOBJ)
	{
		PVRSRVUnlockMutex(gc->psRenderSurface->hMutex);

		GLES1_TIME_STOP(GLES1_TIMER_ELEMENT_POINTS_TIME+mode);
		GLES1_TIME_STOP(GLES1_TIMES_glDrawElements);
			
		return;
	}

#if defined(GLES1_EXTENSION_MAP_BUFFER)
	/* Check whether any buffer object is mapped */
	if(psVAOMachine->ui32ControlWord & ATTRIBARRAY_MAP_BUFOBJ)
	{
		PVRSRVUnlockMutex(gc->psRenderSurface->hMutex);

		SetError(gc, GL_INVALID_OPERATION);

		GLES1_TIME_STOP(GLES1_TIMER_ELEMENT_POINTS_TIME+mode);
		GLES1_TIME_STOP(GLES1_TIMES_glDrawElements);
		
		return;
	}
#endif /* defined(GLES1_EXTENSION_MAP_BUFFER) */


	if(VAO_INDEX_BUFFER_OBJECT(gc) && (type == GL_UNSIGNED_SHORT))
	{
		IMG_UINT32 ui32Offset = (IMG_UINT32)GLES1_BUFFER_OFFSET(indices);
		
		/* Can't use 16 bit indices with 8 bit offset into index buffer */
		if(ui32Offset & 1)
		{
			PVRSRVUnlockMutex(gc->psRenderSurface->hMutex);
		
			SetError(gc, GL_INVALID_OPERATION);

			GLES1_TIME_STOP(GLES1_TIMER_ELEMENT_POINTS_TIME+mode);
			GLES1_TIME_STOP(GLES1_TIMES_glDrawElements);
			
			return;
		}
	}


	if (VAO_IS_ZERO(gc)) /* This setup is only for the DEFAULT VAO */
	{
		if(psVAOMachine->ui32ControlWord & ATTRIBARRAY_SOURCE_VARRAY)
		{
			DetermineMinAndMaxIndices(gc, (IMG_UINT32)count, type, indices, &ui32MinIndex, &ui32MaxIndex);

			ui32VertexStart = ui32MinIndex;
			ui32VertexCount = ui32MaxIndex - ui32MinIndex + 1;

			if(VAO_INDEX_BUFFER_OBJECT(gc) && (GL_UNSIGNED_BYTE != type)) 
			{
				ui32VertexCount += ui32VertexStart;
				ui32VertexStart = 0;
			}
		}
		else
		{
			/* Because all the vertex data comes from buffer objects (or is copied from current state), there is 
			 * no need to know the vertex range.
			 */
			ui32VertexStart = 0;
			ui32VertexCount = 0;
		}
	}
	else
	{
	    /* Because all the vertex data comes from buffer objects (or is copied from current state), there is 
		 * no need to know the vertex range.
		 */
	    ui32VertexStart = 0;
		ui32VertexCount = 0;
	}

	/* Pick draw functions */
	pfnDrawElements = PickDrawElementsProc(gc, mode, (IMG_UINT32)type, (IMG_UINT32)count, ui32VertexCount, ui32MaxIndex);

	/* Attach all used resources to the current surface */
	AttachAllUsedResourcesToCurrentSurface(gc);


	/* All 8-bit indices must be promoted to 16-bit */
	if(GL_UNSIGNED_BYTE == type)
	{
		pui16NewElements = TransformIndicesTo16Bits(gc, (IMG_UINT32)count, type, indices);

		if(!pui16NewElements)
		{
			goto StopTimerAndReturn;
		}

		bIndicesWerePromoted = IMG_TRUE;

		type = GL_UNSIGNED_SHORT;
		pui16Elements = (const IMG_UINT16 *)pui16NewElements;
	}
	else if (VAO_INDEX_BUFFER_OBJECT(gc) && pfnDrawElements != DrawElementsIndexBO)
	{
		IMG_UINT32 ui32IndexOffset = (IMG_UINT32)GLES1_BUFFER_OFFSET(indices) >> 1;
		
		pui16Elements = (const IMG_UINT16 *)psVAOMachine->psActiveVAO->psBoundElementBuffer->psMemInfo->pvLinAddr + ui32IndexOffset;
	}


	/* Call the actual draw element function */
	(*pfnDrawElements)(gc, mode, 0, (IMG_UINT32)count, ui32NumIndices, type, pui16Elements, ui32VertexStart, ui32VertexCount); 

	if(bIndicesWerePromoted) 
	{
		GLES1Free(IMG_NULL, pui16NewElements);
	}

	/*
		Update vertex and index buffers committed primitive offset
	*/
	CBUF_UpdateVIBufferCommittedPrimOffsets(gc->apsBuffers, &gc->psRenderSurface->bPrimitivesSinceLastTA, (IMG_VOID *)gc, KickLimit_ScheduleTA);

StopTimerAndReturn:

	PVRSRVUnlockMutex(gc->psRenderSurface->hMutex);

	GLES1_TIME_STOP(GLES1_TIMER_ELEMENT_POINTS_TIME+mode);
	GLES1_TIME_STOP(GLES1_TIMES_glDrawElements);

	GLES1_PROFILE_INCREMENT_DRAWELEMENTS_CALLCOUNT(mode);
	GLES1_PROFILE_INCREMENT_DRAWELEMENTS_VERTEXCOUNT(mode, (IMG_UINT32)count);

	GLES1_PROFILE_ADD_STATE_METRIC;
}

#if defined(GLES1_EXTENSION_MULTI_DRAW_ARRAYS)

/*
 * Implementation of GL_EXT_multi_draw_arrays
 *
 */

typedef IMG_VOID (*PFNMultiDrawVArray) (GLES1Context *gc, GLenum eMode, IMG_UINT32 * ui32First, IMG_UINT32 * pui32Count,
										IMG_UINT32 ui32NumIndices, GLenum eType, const IMG_VOID * *elements,
										IMG_UINT32 ui32VertexStart, IMG_UINT32 ui32VertexCount, 
										IMG_UINT32 ui32PrimCount);


static IMG_VOID MultiDrawArraysAutoIndices(GLES1Context *gc, GLenum eMode, IMG_UINT32 *pui32First, IMG_UINT32 * pui32Count,
										   IMG_UINT32 ui32NumIndices, GLenum eType, const IMG_VOID * *elements,
										   IMG_UINT32 ui32VertexStart, IMG_UINT32 ui32VertexCount, 
										   IMG_UINT32 ui32PrimCount)
{
	IMG_UINT32 i, j; 

	/* This function autogenerates the indices */
	GLES1_ASSERT(!elements);
	PVR_UNREFERENCED_PARAMETER(elements);
	PVR_UNREFERENCED_PARAMETER(eType);
	PVR_UNREFERENCED_PARAMETER(eMode);

	/* The type must be natively supported by the hardware */
	GLES1_ASSERT(primDirectIndex[eType]);

	if(ui32VertexCount<=GLES1_NUM_STATIC_INDICES && CreateSequentialStaticIndices(gc))
	{
		/* Check the vertex buffer space, flush when necessary */
		GetVertexIndexBufferSpace(gc, 0, ui32VertexCount, sizeof(IMG_UINT16));
		
		/* Setup stream addresses and copy the vertices attributes if required */
		CopyVArrayData(gc, ui32VertexStart, ui32VertexCount, IMG_TRUE);

		CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER);

		for(i = 0; i < ui32PrimCount; i++)
		{
			IMG_DEV_VIRTADDR sIndicesVAddrBase;
			
			sIndicesVAddrBase.uiAddr = gc->psSharedState->psSequentialStaticIndicesMemInfo->sDevVAddr.uiAddr;

			sIndicesVAddrBase.uiAddr += (pui32First[i] - ui32VertexStart)*sizeof(IMG_UINT16);  
			
			/* Emit state */
			GLES1EmitState(gc, pui32Count[i], sIndicesVAddrBase, 0);
		}
	}
	else
	{
		IMG_UINT16 *pui16Indices, *pui16IndexBase;
		IMG_UINT32 ui32IndexDWords;

		/* Check the vertex and index buffer space, flush when necessary */
		/* We are adding Primcount indices, so that if there is an odd number of indices in a primitive, we will definitely have
		 * enough space to waste 16 bits per primitive when we update the bufferpos in DWORDS increments.
		 */
		GetVertexIndexBufferSpace(gc, ui32NumIndices + ui32PrimCount, ui32VertexCount, sizeof(IMG_UINT16));

		/* Setup stream addresses and copy the vertices attributes if required */
		CopyVArrayData(gc, ui32VertexStart, ui32VertexCount, IMG_TRUE);

		CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER);

		for(i = 0; i < ui32PrimCount; i++)
		{	
			GLES1_TIME_START(GLES1_TIMER_INDEX_DATA_GENERATE_COPY);

			/* Round up to nearest 32 bits */
			ui32IndexDWords = (pui32Count[i] + 1) >> 1;

			pui16IndexBase = pui16Indices = (IMG_UINT16 *)CBUF_GetBufferSpace(gc->apsBuffers, ui32IndexDWords, CBUF_TYPE_INDEX_DATA_BUFFER, IMG_FALSE);
			if (pui16IndexBase == IMG_NULL)
			{
				PVR_DPF((PVR_DBG_ERROR,"MultiDrawArraysAutoIndices: Cannot get enough space for indices for primitive %d ", i));
				GLES1_TIME_STOP(GLES1_TIMER_INDEX_DATA_GENERATE_COPY);
				break;
			}

			/* 
				write indices for current primitive; we can't do this for all primitives in one go
				because we have to take into account the case where there are gaps between sets of indices, e.g.:
					prim 1: 0, 1, 2
					prim 2: 5, 6, 7
				In this example there is a gap of two indices (3 and 4), but the index buffer has only six elements.
			*/
			for(j = 0; j < pui32Count[i]; j++)
			{
				*pui16Indices++ = (IMG_UINT16)((j + (pui32First[i] - ui32VertexStart))); 	
			}

			GLES1_TIME_STOP(GLES1_TIMER_INDEX_DATA_GENERATE_COPY);			
			
			CBUF_UpdateBufferPos(gc->apsBuffers, ui32IndexDWords, CBUF_TYPE_INDEX_DATA_BUFFER);
			
			/* Emit state */
			GLES1EmitState(gc, pui32Count[i], CBUF_GetBufferDeviceAddress(gc->apsBuffers, (IMG_VOID *)pui16IndexBase, CBUF_TYPE_INDEX_DATA_BUFFER), 0);

			GLES1_INC_COUNT(GLES1_TIMER_INDEX_DATA_COUNT, ui32IndexDWords);
		}
	}
}

static IMG_VOID MultiDrawBatchOnVBuffer(GLES1Context *gc, GLenum eMode, IMG_UINT32 *pui32First, IMG_UINT32 * pui32CountIn,
										   IMG_UINT32 ui32NumIndices, GLenum eType, const IMG_VOID **elements,
										   IMG_UINT32 ui32VertexStart, IMG_UINT32 ui32VertexCount, 
										   IMG_UINT32 ui32PrimCount)
{
	IMG_UINT32 i;
	IMG_UINT32 ui32IndexStart;
	IMG_UINT32 ui32RewindCount;
	IMG_UINT32 ui32BatchSize; 
	const IMG_UINT16 *pui16Indices = (const IMG_UINT16 *)elements;
	IMG_BOOL bDeIndex = (elements)?IMG_TRUE : IMG_FALSE, bAttributesWereMangled = IMG_FALSE;

	GLESBufferObject *apsBufObjs[GLES1_MAX_ATTRIBS_ARRAY];
	GLES1VertexArrayObjectMachine *psVAOMachine = &(gc->sVAOMachine);
	GLES1AttribArrayPointerMachine *psAPMachine;

	PVR_UNREFERENCED_PARAMETER(ui32VertexStart);
	PVR_UNREFERENCED_PARAMETER(ui32VertexCount);
	PVR_UNREFERENCED_PARAMETER(eType);
	PVR_UNREFERENCED_PARAMETER(ui32NumIndices);

	/*
	 * If any of the attributes comes from a buffer object,
	 * we have to reset the stream pointers because we are going to de-index.
	 * This is our way to punish the bad bad app that forced us to go through this code path.
	 */
	if(psVAOMachine->ui32ControlWord & ATTRIBARRAY_SOURCE_BUFOBJ)
	{
		bAttributesWereMangled = IMG_TRUE;
		/*
		 * Make a backup copy of the buffer objects pointers of the attributes.
		 * Set up the attrib pointers to force CopyArrayDataDeIndex to copy the vertex attributes from
		 * the buffer objects into the vertex buffer.
		 */
		for(i = 0; i < psVAOMachine->ui32NumItemsPerVertex; i++)
		{
			/* Get psAPMachine & Assert it exists */
			psAPMachine = psVAOMachine->apsPackedAttrib[i];
			GLES1_ASSERT( psAPMachine );

			apsBufObjs[i] = psAPMachine->psState->psBufObj;
			
			if(apsBufObjs[i])
			{
				psAPMachine->psState->psBufObj = 0;
			}
		}

		/* Only set context's dirty state, no need for VAO */
		gc->ui32DirtyMask |= GLES1_DIRTYFLAG_VAO_ATTRIB_STREAM;
			

		/* Validate all the states at this point */
		if(ValidateState(gc)!=GLES1_NO_ERROR)
		{
			PVR_DPF((PVR_DBG_ERROR,"MultiDrawBatchOnVBuffer: ValidateState() failed"));
			
			goto ResetAttribsAndReturn;
		}

		for(i = 0; i < psVAOMachine->ui32NumItemsPerVertex; i++)
		{
			/* Get psAPMachine & Assert it exists */
			psAPMachine = psVAOMachine->apsPackedAttrib[i];
			GLES1_ASSERT( psAPMachine );

			/*
			 * The value of pui8CopyPointer is actually an offset based on the buffer object,
			 * so we need to add it to get a real pointer.
			 */
			if(apsBufObjs[i] && !psAPMachine->bIsCurrentState)
			{
				psAPMachine->pui8CopyPointer += (IMG_UINT32)apsBufObjs[i]->psMemInfo->pvLinAddr;
			}
		}
	}


	for(i = 0; i < ui32PrimCount; i++)
	{
		ui32BatchSize  = pui32CountIn[i];
		ui32IndexStart = 0;

		/* work out the batch size */
		if(gc->ui32VertexSize)
		{
			ui32BatchSize = MIN(ui32BatchSize, (MAX_VBUFFER - gc->ui32VertexRCSize - gc->ui32VertexAlignSize) / gc->ui32VertexSize);
		}

		ui32BatchSize = MIN(64*1024, ui32BatchSize); 

		/* 
		 * After the first batch has been drawn, it needs some vertices overlaped for the second batch, 
		 * the following function calculates this rewind count, at the same time, modifying uBatchSize
		 * to a proper figure.
		 */
		if(ui32BatchSize < pui32CountIn[i])
		{
			ui32RewindCount = GetVArrayBatchRewindCount(eMode, &ui32BatchSize);
		}
		else
		{
			ui32RewindCount = 0;
		}


		switch(eMode)
		{
			case GL_POINTS:          /* Intentional fall-through */
			case GL_TRIANGLES:
			case GL_LINES:
			case GL_TRIANGLE_STRIP:
			case GL_LINE_STRIP:
			case GL_LINE_LOOP:
			{
				IMG_UINT32 ui32Count, ui32BatchIndices;

				do
				{	
					ui32Count = MIN(ui32BatchSize, pui32CountIn[i] - ui32IndexStart);
						ui32BatchIndices = GetNumIndices(eMode, ui32Count);

					if(ui32BatchIndices)
					{
						/*check the vertex and index buffer space, flush when necessary */
						GetVertexIndexBufferSpace(gc, ui32BatchIndices, ui32Count, sizeof(IMG_UINT16));

						if(bDeIndex && elements != NULL && elements[i] != IMG_NULL)
						{
							CopyVArrayDataDeindex(gc, ui32IndexStart, ui32Count, (const IMG_VOID *) elements[i]);
						}
						else
						{
							CopyVArrayData(gc, ui32IndexStart + pui32First[i], ui32Count, IMG_TRUE);
						}

						if(GL_LINE_LOOP == eMode)
						{
							/*
							* We override the line mode because we don't want to connect the last vertex of each
							* batch to the first vertex of each bach.
							*/
								(WriteIndices[GL_LINE_STRIP])(gc, GL_LINE_STRIP, 0, ui32Count, IMG_NULL);
						}
						else
						{
								(WriteIndices[eMode])(gc, eMode, 0, ui32Count, IMG_NULL);
						}
					}
				
					ui32IndexStart += ui32Count - ui32RewindCount;

					CBUF_UpdateVIBufferCommittedPrimOffsets(gc->apsBuffers, &gc->psRenderSurface->bPrimitivesSinceLastTA, (IMG_VOID *)gc, KickLimit_ScheduleTA);

				} while(ui32IndexStart + ui32RewindCount< pui32CountIn[i]);

				if(GL_LINE_LOOP == eMode)
				{
					/* Connect the last vertex with the first one */
					IMG_UINT16 pui16Tmpindex[2];
					IMG_UINT32 ui32PreviousIndex = (ui32RewindCount == 0) ? ui32IndexStart - 1 : ui32IndexStart;
				
					/* Check the vertex and index buffer space, flush when necessary */
					GetVertexIndexBufferSpace(gc, 2, 2, sizeof(IMG_UINT16));

					if(elements)
					{
						pui16Tmpindex[0] = pui16Indices[ui32PreviousIndex+pui32First[i]];
						pui16Tmpindex[1] = pui16Indices[pui32First[i]];
					}
					else
					{
						pui16Tmpindex[0] = (IMG_UINT16) (ui32PreviousIndex + pui32First[i]);
						pui16Tmpindex[1] = (IMG_UINT16) pui32First[i];
					}

					CopyVArrayDataDeindex(gc, 0, 2, pui16Tmpindex);

					(WriteIndices[GL_LINE_STRIP])(gc, GL_LINE_STRIP, 0, 2, IMG_NULL);

					CBUF_UpdateVIBufferCommittedPrimOffsets(gc->apsBuffers, &gc->psRenderSurface->bPrimitivesSinceLastTA, (IMG_VOID *)gc, KickLimit_ScheduleTA);
				}

				break;
			}
			case GL_TRIANGLE_FAN:
			{
				IMG_UINT16 * IMG_RESTRICT pui16Tmpindex;
				IMG_UINT32 j, ui32Count, ui32BatchIndices;

				pui16Tmpindex = GLES1Malloc(gc, ui32BatchSize * sizeof(IMG_UINT16));

				if(!pui16Tmpindex)
				{
					PVR_DPF((PVR_DBG_WARNING,"MultiDrawBatchOnVBuffer: Out of memory for pui32Tmpindex"));

					SetError(gc, GL_OUT_OF_MEMORY);

					goto ResetAttribsAndReturn;
				}

				do
				{
					ui32Count = MIN(ui32BatchSize, pui32CountIn[i] - ui32IndexStart);
					ui32BatchIndices = GetNumIndices(eMode, ui32Count);

					if(ui32BatchIndices)
					{
						/* Check the vertex and index buffer space, flush when necessary */
						GetVertexIndexBufferSpace(gc, ui32Count, ui32Count, sizeof(IMG_UINT16));

						/* Check that we don't overflow pui16Tmpindex */
						GLES1_ASSERT(ui32Count <= ui32BatchSize);
				
						if(elements)
						{
							const IMG_UINT16 *pui16Src = &pui16Indices[ui32IndexStart + pui32First[i]];

							pui16Tmpindex[0] = pui16Indices[pui32First[i]];

							for(j = 1; j < ui32Count; ++j)
							{
								pui16Tmpindex[j] = pui16Src[j];
							}
						}
						else
						{
							/* for DrawArrays */
							pui16Tmpindex[0] = (IMG_UINT16) pui32First[i];

							for(j = 1; j < ui32Count; ++j)
							{
								pui16Tmpindex[j] = (IMG_UINT16)(ui32IndexStart + pui32First[i] + j);
							}
						}

						CopyVArrayDataDeindex(gc, 0, ui32Count, pui16Tmpindex);

						(WriteIndices[eMode])(gc, eMode, 0, ui32Count, IMG_NULL);
					}

					ui32IndexStart += (ui32Count - ui32RewindCount);

					CBUF_UpdateVIBufferCommittedPrimOffsets(gc->apsBuffers, &gc->psRenderSurface->bPrimitivesSinceLastTA, (IMG_VOID *)gc, KickLimit_ScheduleTA);

				} while(ui32IndexStart + ui32RewindCount < pui32CountIn[i]);

				GLES1Free(IMG_NULL, pui16Tmpindex);

				break;
			}
			default:
			{
				PVR_DPF((PVR_DBG_ERROR,"MultiDrawBatchOnVBuffer: Bad mode (0x%X) - shouldn't be here", eMode));
			}
		} /* switch */

	} /* for loop */

ResetAttribsAndReturn:

	if(bAttributesWereMangled)
	{
		/*
		 * Restore the pointers to buffer objects and set up the dirty flags to recompute the stream pointers.
		 */
	    for(i = 0; i < psVAOMachine->ui32NumItemsPerVertex; i++)
		{
			psVAOMachine->apsPackedAttrib[i]->psState->psBufObj = apsBufObjs[i];
		}

		/* Only set context's dirty state, no need for VAO's */
		gc->ui32DirtyMask |= GLES1_DIRTYFLAG_VAO_ATTRIB_STREAM;
	}

}



static IMG_VOID MultiDrawVertexArray(GLES1Context *gc, GLenum eMode, IMG_UINT32 *pui32First, IMG_UINT32 * pui32Count,
									 IMG_UINT32 ui32NumIndices, GLenum eType, const IMG_VOID * *elements,
									 IMG_UINT32 ui32VertexStart, IMG_UINT32 ui32VertexCount, 
									 IMG_UINT32 ui32PrimCount)
{
	IMG_UINT32 i, ui32IndexDWords;
	IMG_BOOL   bIndicesAreZeroBased = (elements==IMG_NULL)?IMG_TRUE:IMG_FALSE;

	GLES1_ASSERT(!elements || eType != GL_UNSIGNED_BYTE);
	PVR_UNREFERENCED_PARAMETER(eType);


	if (bIndicesAreZeroBased && eMode==GL_LINE_STRIP &&
		((ui32VertexCount*2)<=GLES1_NUM_STATIC_INDICES) && CreateLineStripStaticIndices(gc))
	{
		/* Check the vertex buffer space, flush when necessary */
		GetVertexIndexBufferSpace(gc, 0, ui32VertexCount, sizeof(IMG_UINT16));

		/* Setup stream addresses and copy the vertices attributes if required */
		CopyVArrayData(gc, ui32VertexStart, ui32VertexCount, IMG_TRUE);

		CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER);

		for(i = 0; i < ui32PrimCount; i++)
		{
			IMG_DEV_VIRTADDR sIndicesVAddrBase;
			IMG_UINT32 ui32AdjustedCount, ui32AdjustedStart;
			
			ui32AdjustedStart = (pui32First[i] - ui32VertexStart) * 2;
			ui32AdjustedCount = GetNumIndices(GL_LINE_STRIP, pui32Count[i]);

			sIndicesVAddrBase.uiAddr = gc->psSharedState->psLineStripStaticIndicesMemInfo->sDevVAddr.uiAddr;

			sIndicesVAddrBase.uiAddr += (ui32AdjustedStart * sizeof(IMG_UINT16));  
			
			/* Emit state */
			GLES1EmitState(gc, ui32AdjustedCount, sIndicesVAddrBase, 0);
		}
	}
	else
	{
		/* Check the vertex and index buffer space, flush when necessary */
		GetVertexIndexBufferSpace(gc, ui32NumIndices, ui32VertexCount, sizeof(IMG_UINT16));

		/* Setup stream addresses and copy the vertices attributes if required */
		CopyVArrayData(gc, ui32VertexStart, ui32VertexCount, bIndicesAreZeroBased);
		
		for(i = 0; i < ui32PrimCount; i++)
		{

			if(i > 0)
			{
				if(eMode == GL_LINE_LOOP || eMode == GL_LINE_STRIP)
				{
					ui32IndexDWords = (pui32Count[i]*2 + 2) >> 1;
				} 
				else
				{
					/* Round up mem usage to nearest 32 bits */
					ui32IndexDWords = (pui32Count[i] + 1) >> 1;
				}

				/* update position in the circular buffer */
				gc->pui32IndexData = CBUF_GetBufferSpace(gc->apsBuffers, ui32IndexDWords, CBUF_TYPE_INDEX_DATA_BUFFER, IMG_FALSE);
			}

			/* Write indices, may be 16 or 32-bit */
			if (bIndicesAreZeroBased)
			{
				(WriteIndices[eMode])(gc, eMode, bIndicesAreZeroBased ? (pui32First[i] - ui32VertexStart) : 0, pui32Count[i], IMG_NULL);
			}
			else if (elements != NULL && elements[i] != IMG_NULL)
			{
				(WriteIndices[eMode])(gc, eMode, bIndicesAreZeroBased ? (pui32First[i] - ui32VertexStart) : 0, pui32Count[i], elements[i]);
			}
		}
	}
}

static IMG_VOID MultiDrawElementsDeindex(GLES1Context *gc, GLenum eMode, IMG_UINT32 *pui32First, IMG_UINT32 * pui32Count,
										 IMG_UINT32 ui32NumIndices, GLenum eType, const IMG_VOID **elements,
										 IMG_UINT32 ui32VertexStart, IMG_UINT32 ui32VertexCount, 
										 IMG_UINT32 ui32PrimCount)
{
	IMG_UINT32 i, ui32IndexDWords, j, ui32Offset = 0;
	IMG_UINT16 * pui16Tmpindex;
	IMG_UINT16 * pui16DeIndices;
	const IMG_UINT16 **ppui16Elements;

	GLES1_ASSERT(VAO(gc));

	GLES1_ASSERT(elements);

	PVR_UNREFERENCED_PARAMETER(pui32First);
	PVR_UNREFERENCED_PARAMETER(eType);
	PVR_UNREFERENCED_PARAMETER(ui32VertexStart);
	PVR_UNREFERENCED_PARAMETER(ui32VertexCount);


	/* We should not deindex BOs unless v and i data cannot fit in */
	GLES1_ASSERT(!(gc->sVAOMachine.ui32ControlWord & ATTRIBARRAY_SOURCE_BUFOBJ));

	/* Check the vertex and index buffer space, flush when necessary */
	GetVertexIndexBufferSpace(gc, ui32NumIndices, ui32VertexCount, sizeof(IMG_UINT16));

	/*
	 * Copy all indices in pui16DeIndices, then use it to gather together all the vertices
	 * we need to draw 
	 */
	ppui16Elements = (const IMG_UINT16 **) elements;
	pui16DeIndices = GLES1Malloc(gc, ui32NumIndices * sizeof(IMG_UINT16));

	if(!pui16DeIndices)
	{
		PVR_DPF((PVR_DBG_WARNING,"MultiDrawElementsDeindex Out of memory for pui16DeIndices"));
		SetError(gc, GL_OUT_OF_MEMORY);
		return;
	}

	pui16Tmpindex = pui16DeIndices;

	for(i = 0; i < ui32PrimCount; i++)
	{
		const IMG_UINT16 * Temp = ppui16Elements[i];

		for(j = 0; j < pui32Count[i]; j++)
		{
			*pui16Tmpindex = Temp[j];
			pui16Tmpindex++;
		}
	}
	
	CopyVArrayDataDeindex(gc, 0, ui32NumIndices, (IMG_VOID *) pui16DeIndices);


	for(i = 0; i < ui32PrimCount; i++)
	{
		if(i > 0)
		{
			if(eMode == GL_LINE_LOOP || eMode == GL_LINE_STRIP)
			{
				ui32IndexDWords = (pui32Count[i]*2 + 2) >> 1;
			} 
			else
			{
				/* Round up mem usage to nearest 32 bits */
				ui32IndexDWords = (pui32Count[i] + 1) >> 1;
			}

			/* update position in the circular buffer */
			gc->pui32IndexData = CBUF_GetBufferSpace(gc->apsBuffers, ui32IndexDWords, CBUF_TYPE_INDEX_DATA_BUFFER, IMG_FALSE);
		}

		/* Write 16-bit indices */
		(WriteIndices[eMode])(gc, eMode, ui32Offset, pui32Count[i], IMG_NULL);

		ui32Offset += pui32Count[i];
	}

	GLES1Free(IMG_NULL, pui16DeIndices);
}


static IMG_VOID MultiDrawElementsIndexBO(GLES1Context *gc, GLenum eMode, IMG_UINT32 *pui32First, IMG_UINT32 * pui32Count,
										 IMG_UINT32 ui32NumIndices, GLenum eType, const IMG_VOID * *elements,
										 IMG_UINT32 ui32VertexStart, IMG_UINT32 ui32VertexCount, 
										 IMG_UINT32 ui32PrimCount)
{
    PVRSRV_CLIENT_MEM_INFO *psMemInfo;
	IMG_UINT32 ui32Offset; 
	IMG_DEV_VIRTADDR uIndexAddress;
	IMG_UINT32 ui32BatchSize, ui32RewindCount, ui32Count, ui32IndexStart, i;
	GLES1VertexArrayObjectMachine *psVAOMachine = &(gc->sVAOMachine);
	GLESBufferObject *psIndexBO = psVAOMachine->psBoundElementBuffer;


	PVR_UNREFERENCED_PARAMETER(pui32First);
	PVR_UNREFERENCED_PARAMETER(eType);
	PVR_UNREFERENCED_PARAMETER(eMode);
	PVR_UNREFERENCED_PARAMETER(ui32NumIndices);

	GLES1_ASSERT(psIndexBO->psMemInfo);

	psMemInfo = psIndexBO->psMemInfo;

	/* Check the vertex and index buffer space, flush when necessary */
	GetVertexIndexBufferSpace(gc, 0, ui32VertexCount, 0);

	/* Setup stream addresses and copy the vertices attributes if required */
	CopyVArrayData(gc, ui32VertexStart, ui32VertexCount, IMG_FALSE);

   	for(i = 0; i < ui32PrimCount; i++)
	{
		ui32Offset = (IMG_UINT32) GLES1_BUFFER_OFFSET(elements[i]);

		uIndexAddress.uiAddr = psMemInfo->sDevVAddr.uiAddr;
		uIndexAddress.uiAddr += (IMG_UINT32)ui32Offset;
		
		if(ui32Offset > psMemInfo->uAllocSize)
		{
			PVR_DPF((PVR_DBG_ERROR,"Index offset %u is larger than index buffer size %u", ui32Offset, psMemInfo->uAllocSize));
		}
		
		/* lock the circular buffer - for the first iteration it's done by GetVertexIndexBufferSpace */
		if(i > 0)
		{
			CBUF_GetBufferSpace(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER, IMG_FALSE);
		}
		CBUF_UpdateBufferPos(gc->apsBuffers, 0, CBUF_TYPE_INDEX_DATA_BUFFER);

		/* 
		 * After the first batch has been drawn, it needs some vertices overlaped for the second batch, 
		 * the following function calculates this rewind count, at the same time, modifying uBatchSize
		 * to a proper figure.
		 */
		
		ui32BatchSize = MIN(pui32Count[i], MAX_INDICES_PER_PRIMBLOCK(sizeof(IMG_UINT16)));

		/* check if indices batching is needed */
		if(ui32BatchSize < pui32Count[i])
		{
			ui32RewindCount = GetVArrayBatchRewindCount(eMode, &ui32BatchSize);
		}
		else
		{
			ui32RewindCount = 0;
		}
		ui32IndexStart = 0;

		do
		{	
			ui32Count = MIN(ui32BatchSize, pui32Count[i] - ui32IndexStart);
			
			/* Emit state */
			GLES1EmitState(gc, ui32Count, uIndexAddress, 0);
			
			ui32IndexStart += ui32Count - ui32RewindCount;
			uIndexAddress.uiAddr += ((ui32Count - ui32RewindCount) * sizeof(IMG_UINT16));
			
		} while(ui32IndexStart + ui32RewindCount < pui32Count[i]);
	}
}

static PFNMultiDrawVArray PickMultiDrawArraysProc(GLES1Context *gc, GLenum eMode, IMG_UINT32 ui32Count)
{
	PFNMultiDrawVArray multiDrawVArray;

	IMG_BOOL bVertexFit = IMG_TRUE;
	IMG_BOOL bIndexFit = IMG_TRUE;
	IMG_BOOL bDirectIndices = primDirectIndex[eMode];


	if((ui32Count * sizeof(IMG_UINT16)) > MAX_IBUFFER || (ui32Count > 64*1024))
	{
		bIndexFit = IMG_FALSE;
	}

	if((ui32Count * gc->ui32VertexSize + gc->ui32VertexRCSize + gc->ui32VertexAlignSize) > MAX_VBUFFER)
	{
		bVertexFit = IMG_FALSE;
	}

	if(bVertexFit && 
	   bIndexFit && 
	   bDirectIndices)
	{
		multiDrawVArray = MultiDrawArraysAutoIndices;

		return multiDrawVArray;
	}

	if(bVertexFit && bIndexFit)
	{
		multiDrawVArray = MultiDrawVertexArray;
	}
	else
	{
		multiDrawVArray = MultiDrawBatchOnVBuffer;
	}

	return multiDrawVArray;
}



static PFNMultiDrawVArray PickMultiDrawElementsProc(GLES1Context *gc, GLenum eMode, GLenum eType,
													IMG_UINT32 ui32NumIndices, IMG_UINT32 ui32VertexCount, IMG_UINT32 ui32MaxIndex)
{
	IMG_UINT32 ui32SizePerIndex = sizeof(IMG_UINT16);
	PFNMultiDrawVArray multiDrawVArray;
	IMG_BOOL bIndexBO,
	         bSourceBO,
	         bIndexFit  = IMG_TRUE,
	         bVertexFit = IMG_TRUE,
			 bDeindexCase = IMG_FALSE;

	GLES1VertexArrayObjectMachine *psVAOMachine = &(gc->sVAOMachine);
	GLESBufferObject *psIndexBO = psVAOMachine->psBoundElementBuffer;		

	/* Setup bIndexBO & bSourceBO */
	bIndexBO  = (psIndexBO) ? IMG_TRUE : IMG_FALSE;
	bSourceBO = (psVAOMachine->ui32ControlWord & ATTRIBARRAY_SOURCE_BUFOBJ) ? IMG_TRUE : IMG_FALSE;


	/* We cannot use an index buffer object in these cases */
	if (eMode==GL_LINE_STRIP ||
		eMode==GL_LINE_LOOP  ||
		((eMode==GL_TRIANGLE_FAN) && (ui32NumIndices > MAX_INDICES_PER_PRIMBLOCK(sizeof(IMG_UINT16)))) ||
		eType==GL_UNSIGNED_BYTE)
	{
		bIndexBO = IMG_FALSE;
	}

	if(!bIndexBO && !bSourceBO)
	{
		if(ui32VertexCount > 2*ui32NumIndices)
		{
			bDeindexCase = IMG_TRUE;
		}
	}



	if(!bIndexBO && ((ui32NumIndices * ui32SizePerIndex > MAX_IBUFFER) || (ui32MaxIndex > 64 * 1024)))
	{
		bIndexFit = IMG_FALSE;
	}

	if(ui32VertexCount * gc->ui32VertexSize + gc->ui32VertexRCSize + gc->ui32VertexAlignSize > MAX_VBUFFER)
	{
		bVertexFit = IMG_FALSE;
	}

	if(bIndexBO)
	{
		if(bVertexFit) 
		{
			multiDrawVArray = MultiDrawElementsIndexBO;	
			
			return multiDrawVArray;
		}
	}


	if(bVertexFit && bIndexFit)
	{
		if(bDeindexCase)
		{
			multiDrawVArray = MultiDrawElementsDeindex;
		}
		else
		{
			multiDrawVArray = MultiDrawVertexArray;
		}
	}
	else
	{
		multiDrawVArray = MultiDrawBatchOnVBuffer;
	}

	return multiDrawVArray;
}


/***********************************************************************************
 Function Name      : glMultiDrawArraysEXT
 Inputs             : mode, first, count, primcount
 Outputs            : -
 Returns            : -
 Description        : ENTRYPOINT: Draws primcount primitives of type mode using attrib arrays. 
					  Will validate as necessary, then send control, state and attrib 
					  data to HW.
************************************************************************************/
GL_API_EXT void GL_APIENTRY glMultiDrawArraysEXT(GLenum mode, GLint * first, GLsizei * count, GLsizei primcount)
{
	PFNMultiDrawVArray pfnMultiDrawArrays;
	IMG_UINT32 ui32NumIndices = 0;
	IMG_UINT32 ui32NoClears = 0;
	IMG_UINT32 ui32MinFirst = 0xFFFFFFFF, ui32MaxCount = 0;
	IMG_INT32 i;
	IMG_UINT32 * pui32ActualCount;
	IMG_BOOL bIsHWSupported = primDirectIndex[mode];
	GLES1VertexArrayObjectMachine *psVAOMachine;
	GLES1VertexArrayObject *psVAO;

	__GLES1_GET_CONTEXT();

	PVR_DPF((PVR_DBG_CALLTRACE,"glMultiDrawArrays"));

	GLES1_TIME_START(GLES1_TIMES_glMultiDrawArrays);
	GLES1_TIME_START(GLES1_TIMER_ARRAY_POINTS_TIME+(mode & GLES1_TIMER_ARRAY_MODE_MASK));


	/* Setup VAOMachine and VAO */
	psVAOMachine = &(gc->sVAOMachine);
	psVAO = psVAOMachine->psActiveVAO;

	/* Assert VAO is not NULL */
	GLES1_ASSERT(psVAOMachine);
	GLES1_ASSERT(psVAO);


	if(primcount < 0) 
	{
bad_value:
		SetError(gc, GL_INVALID_VALUE);

		GLES1_TIME_STOP(GLES1_TIMER_ARRAY_POINTS_TIME+(mode & GLES1_TIMER_ARRAY_MODE_MASK));
		GLES1_TIME_STOP(GLES1_TIMES_glMultiDrawArrays);

		return;
	}

	if(primcount == 0)
	{
		GLES1_TIME_STOP(GLES1_TIMER_ARRAY_POINTS_TIME+(mode & GLES1_TIMER_ARRAY_MODE_MASK));
		GLES1_TIME_STOP(GLES1_TIMES_glMultiDrawArrays);

		return;
	}
	
	for(i = 0; i < primcount; i++)
	{
		/* check that first and count are positive */
		if(first[i] < 0 || count[i] < 0) 
		{
			goto bad_value;
		}
	}

	if(mode > GL_TRIANGLE_FAN) 
	{
		SetError(gc, GL_INVALID_ENUM);

		GLES1_TIME_STOP(GLES1_TIMER_ARRAY_POINTS_TIME+(mode & GLES1_TIMER_ARRAY_MODE_MASK));
		GLES1_TIME_STOP(GLES1_TIMES_glMultiDrawArrays);

		return;
	}


	pui32ActualCount = GLES1Malloc(gc, sizeof(IMG_UINT32)*(IMG_UINT32)primcount);

	if(!pui32ActualCount)
	{
		PVR_DPF((PVR_DBG_WARNING,"glMultiDrawArrays: Out of memory for ActualCount"));

		SetError(gc, GL_OUT_OF_MEMORY);

		GLES1_TIME_STOP(GLES1_TIMER_ARRAY_POINTS_TIME+mode);
		GLES1_TIME_STOP(GLES1_TIMES_glMultiDrawArrays);

		return;
	}

	for(i = 0; i < primcount; i++)
	{  
		/*  
			this if-else is to cope with an asimmetry:
			- sub-routines for HW supported primitives require adjusted count values,e.g.:
			 2 triangles, primcount = 2, count[0] = 4, count[1] = 5
			 In this case we want actualcount[0] = 3, actualcount[1] = 3, numindices = 9
			- sub-routines for non HW supported primitives (i.e., line strips and loops) use original counts, e.g.:
			 2 lines strips, primcount =2, count[0] = 3, count[1] = 4
			 Then we want: actualcount[0] = 3, actualcount[1] = 4, because the original counts are
			 used by write_line_strips_or_loops, and numindices = 10, as we need more indices 
		*/
		if(bIsHWSupported)
		{
			pui32ActualCount[i] = GetNumIndices(mode, (IMG_UINT32)count[i]);
			ui32NumIndices += (IMG_UINT32)count[i]; 			
		}
		else
		{
			pui32ActualCount[i] = (IMG_UINT32)count[i];
			    ui32NumIndices += GetNumIndices(mode, (IMG_UINT32)count[i]); 
		}

		if((IMG_UINT32)first[i] < ui32MinFirst)
		{
			ui32MinFirst = (IMG_UINT32)first[i];
		}

		if((pui32ActualCount[i] + (IMG_UINT32)first[i]) > ui32MaxCount)
		{
			ui32MaxCount = pui32ActualCount[i] + (IMG_UINT32)first[i];
		}
	}

	/* Only need to copy vertices from minfirst to maxcount */
	ui32MaxCount = ui32MaxCount - ui32MinFirst;		/* PRQA S 3382 */ /* ui32MaxCount is no less than ui32MinFirst. */

	if(ui32NumIndices == 0)
	{
		GLES1_TIME_STOP(GLES1_TIMER_ARRAY_POINTS_TIME+mode);
		GLES1_TIME_STOP(GLES1_TIMES_glMultiDrawArrays);

		return;
	}

	/* All the checking referring to psVAO */
	if((psVAO->ui32CurrentArrayEnables & VARRAY_PROVOKE) == 0)
	{
		GLES1_TIME_STOP(GLES1_TIMER_ARRAY_POINTS_TIME+mode);
		GLES1_TIME_STOP(GLES1_TIMES_glMultiDrawArrays);
		
		return;
	}

	/* Matrix palette must have valid pointers for index and weights */
	if(gc->ui32TnLEnables & GLES1_TL_MATRIXPALETTE_ENABLE)
	{
		if((psVAO->ui32CurrentArrayEnables & (VARRAY_WEIGHTARRAY_ENABLE | VARRAY_MATRIXINDEX_ENABLE)) !=
											 (VARRAY_WEIGHTARRAY_ENABLE | VARRAY_MATRIXINDEX_ENABLE))
		{
			GLES1_TIME_STOP(GLES1_TIMER_ARRAY_POINTS_TIME+mode);
			GLES1_TIME_STOP(GLES1_TIMES_glMultiDrawArrays);
			
			return;
		}
	}

	if(GetFrameBufferCompleteness(gc) != GL_FRAMEBUFFER_COMPLETE_OES)
	{
		SetError(gc, GL_INVALID_FRAMEBUFFER_OPERATION_OES);

		GLES1_TIME_STOP(GLES1_TIMER_ARRAY_POINTS_TIME+mode);
		GLES1_TIME_STOP(GLES1_TIMES_glMultiDrawArrays);

		return;
	}	

	if ((gc->sState.sPolygon.eCullMode==GL_FRONT_AND_BACK) && ((gc->ui32TnLEnables & GLES1_TL_CULLFACE_ENABLE) != 0) &&
	   (mode!=GL_LINES) && (mode!=GL_LINE_LOOP) && (mode!=GL_LINE_STRIP) && (mode!=GL_POINTS))
	{
		/* Return if there's nothing to draw due to front_and_back culling */
		GLES1_TIME_STOP(GLES1_TIMER_ARRAY_POINTS_TIME+mode);
		GLES1_TIME_STOP(GLES1_TIMES_glMultiDrawArrays);

		return;
	}

	HandlePrimitiveTypeChange(gc, mode);

	if(!PrepareToDraw(gc, &ui32NoClears, IMG_TRUE))
	{
		PVR_DPF((PVR_DBG_ERROR,"glDrawArrays: Can't prepare to draw"));

		GLES1_TIME_STOP(GLES1_TIMER_ARRAY_POINTS_TIME+mode);
		GLES1_TIME_STOP(GLES1_TIMES_glMultiDrawArrays);

		return;
	}

	if(gc->ui32DirtyMask || gc->sVAOMachine.psActiveVAO->ui32DirtyMask)
	{
		if(ValidateState(gc)!=GLES1_NO_ERROR)
		{
			PVR_DPF((PVR_DBG_ERROR,"glMultiDrawArrays: ValidateState() failed"));
			PVRSRVUnlockMutex(gc->psRenderSurface->hMutex);
		
			GLES1_TIME_STOP(GLES1_TIMER_ARRAY_POINTS_TIME+mode);
			GLES1_TIME_STOP(GLES1_TIMES_glMultiDrawArrays);

			return;
		}
	}

	/* Check whether any buffer object has undefined memory. 
	   This could cause visual corruption or lockup, so return without drawing. 
	   An OUT_OF_MEMORY error will have been raised at the point of allocation failure.
	 */
	if(psVAOMachine->ui32ControlWord & ATTRIBARRAY_BAD_BUFOBJ)
	{
		GLES1_TIME_STOP(GLES1_TIMER_ARRAY_POINTS_TIME+mode);
		GLES1_TIME_STOP(GLES1_TIMES_glMultiDrawArrays);
		PVRSRVUnlockMutex(gc->psRenderSurface->hMutex);

		return;
	}

#if defined(GLES1_EXTENSION_MAP_BUFFER)
	/* Check whether any buffer object is mapped */
	if(psVAOMachine->ui32ControlWord & ATTRIBARRAY_MAP_BUFOBJ)
	{
		SetError(gc, GL_INVALID_OPERATION);
		PVRSRVUnlockMutex(gc->psRenderSurface->hMutex);

		GLES1_TIME_STOP(GLES1_TIMER_ARRAY_POINTS_TIME+mode);
		GLES1_TIME_STOP(GLES1_TIMES_glMultiDrawArrays);
		
		return;
	}
#endif /* defined(GLES1_EXTENSION_MAP_BUFFER) */


	/* Attach all used resources to the current surface */
	AttachAllUsedResourcesToCurrentSurface(gc);

	pfnMultiDrawArrays = PickMultiDrawArraysProc(gc, mode, ui32NumIndices);

	GLES1_ASSERT(pfnMultiDrawArrays != IMG_NULL);

	(*pfnMultiDrawArrays)(gc, mode, (IMG_UINT32 *) first, pui32ActualCount, 
						  ui32NumIndices, 0, IMG_NULL, 
						  ui32MinFirst, ui32MaxCount, (IMG_UINT32)primcount);


	GLES1Free(IMG_NULL, pui32ActualCount);

	/*
		Update vertex and index buffers committed primitive offset
	*/
	CBUF_UpdateVIBufferCommittedPrimOffsets(gc->apsBuffers, &gc->psRenderSurface->bPrimitivesSinceLastTA, (IMG_VOID *)gc, KickLimit_ScheduleTA);
	
	PVRSRVUnlockMutex(gc->psRenderSurface->hMutex);

	GLES1_TIME_STOP(GLES1_TIMER_ARRAY_POINTS_TIME+mode);
	GLES1_TIME_STOP(GLES1_TIMES_glMultiDrawArrays);

	GLES1_PROFILE_INCREMENT_DRAWARRAYS_CALLCOUNT(mode);
	GLES1_PROFILE_INCREMENT_DRAWARRAYS_VERTEXCOUNT(mode, ui32MaxCount);

	GLES1_PROFILE_ADD_STATE_METRIC;
}



GL_API_EXT void GL_APIENTRY glMultiDrawElementsEXT(GLenum mode, const GLsizei *count,
													GLenum type, const GLvoid* *indices, GLsizei primcount)
{
	IMG_UINT32 ui32NumIndices = 0, ui32MinIndex=0xFFFFFFFF, ui32MaxIndex=0;
	IMG_UINT32 ui32NoClears = 0;
	IMG_UINT32 ui32VertexStart, ui32VertexCount;
	const IMG_VOID **ppvElements = IMG_NULL, **ppvActualIndices = IMG_NULL;
	PFNMultiDrawVArray pfnMultiDrawElements;
	IMG_UINT32 i;
	IMG_UINT32 * pui32ActualCount = IMG_NULL, ui32ActualPrimCount = 0;
	IMG_BOOL bIsHWSupported = primDirectIndex[mode];
	GLES1VertexArrayObjectMachine *psVAOMachine;
	GLES1VertexArrayObject *psVAO;


	__GLES1_GET_CONTEXT();

	PVR_DPF((PVR_DBG_CALLTRACE,"glMultiDrawElements"));

	GLES1_TIME_START(GLES1_TIMES_glMultiDrawElements);
	GLES1_TIME_START(GLES1_TIMER_ELEMENT_POINTS_TIME+(mode & GLES1_TIMER_ARRAY_MODE_MASK));


	/* Setup VAOMachine and VAO */
	psVAOMachine = &(gc->sVAOMachine);
	psVAO = psVAOMachine->psActiveVAO;

	/* Assert VAO is not NULL */
	GLES1_ASSERT(psVAOMachine);
	GLES1_ASSERT(psVAO);


	if(primcount < 0) 
	{
bad_value:
		SetError(gc, GL_INVALID_VALUE);

		GLES1_TIME_STOP(GLES1_TIMER_ELEMENT_POINTS_TIME+(mode & GLES1_TIMER_ARRAY_MODE_MASK));
		GLES1_TIME_STOP(GLES1_TIMES_glMultiDrawElements);

		return;
	}

	if(primcount == 0)
	{
		goto StopTimerAndReturn;
	}
	
	if(mode > GL_TRIANGLE_FAN) 
	{
		SetError(gc, GL_INVALID_ENUM);

		GLES1_TIME_STOP(GLES1_TIMER_ELEMENT_POINTS_TIME+(mode & GLES1_TIMER_ARRAY_MODE_MASK));
		GLES1_TIME_STOP(GLES1_TIMES_glMultiDrawElements);

		return;
	}

	switch(type) 
	{
		case GL_UNSIGNED_BYTE:
		case GL_UNSIGNED_SHORT:
		{
			break;
		}
		default:
		{
			SetError(gc, GL_INVALID_ENUM);
			
			goto StopTimerAndReturn;
		}
	}

	pui32ActualCount = GLES1Malloc(gc, sizeof(IMG_UINT32)*(IMG_UINT32)primcount);

	if(!pui32ActualCount)
	{
		PVR_DPF((PVR_DBG_WARNING,"glMultiDrawElements: Out of memory for ActualCount"));

		SetError(gc, GL_OUT_OF_MEMORY);

		goto StopTimerAndReturn;
	}

	ppvActualIndices = GLES1Malloc(gc, sizeof(IMG_VOID *)*(IMG_UINT32)primcount);

	if(!ppvActualIndices)
	{
		PVR_DPF((PVR_DBG_WARNING,"glMultiDrawElements: Out of memory for ppvActualIndices"));

		SetError(gc, GL_OUT_OF_MEMORY);
		goto StopTimerAndReturn;
	}

	ppvElements = GLES1Malloc(gc, sizeof(IMG_VOID *) * (IMG_UINT32)primcount);

	if(!ppvElements)
	{
		PVR_DPF((PVR_DBG_WARNING,"glMultiDrawElements: Out of memory for ppvNewElements"));

		SetError(gc, GL_OUT_OF_MEMORY);
		goto StopTimerAndReturn;
	}


	/* All the checking referring to psVAO */
	if((psVAO->ui32CurrentArrayEnables & VARRAY_PROVOKE) == 0)
	{
		goto StopTimerAndReturn;
	}

	/* Matrix palette must have valid pointers for index and weights */
	if(gc->ui32TnLEnables & GLES1_TL_MATRIXPALETTE_ENABLE)
	{
		if((psVAO->ui32CurrentArrayEnables & (VARRAY_WEIGHTARRAY_ENABLE | VARRAY_MATRIXINDEX_ENABLE)) != 
		                                     (VARRAY_WEIGHTARRAY_ENABLE | VARRAY_MATRIXINDEX_ENABLE))
		{
			goto StopTimerAndReturn;
		}
	}

	if(GetFrameBufferCompleteness(gc) != GL_FRAMEBUFFER_COMPLETE_OES)
	{
		SetError(gc, GL_INVALID_FRAMEBUFFER_OPERATION_OES);

		goto StopTimerAndReturn;
	}

	
	if ((gc->sState.sPolygon.eCullMode==GL_FRONT_AND_BACK) && ((gc->ui32TnLEnables & GLES1_TL_CULLFACE_ENABLE) != 0) && 
		(mode!=GL_LINES) && (mode!=GL_LINE_LOOP) && (mode!=GL_LINE_STRIP) && (mode!=GL_POINTS))
	{
		/* Return if there's nothing to draw due to front_and_back culling */
		goto StopTimerAndReturn;
	}

	HandlePrimitiveTypeChange(gc, mode);

	if(!PrepareToDraw(gc, &ui32NoClears, IMG_TRUE))
	{
		PVR_DPF((PVR_DBG_ERROR,"glMultiDrawElements: Can't prepare to draw"));

		goto StopTimerAndReturn;
	}

	if(gc->ui32DirtyMask || gc->sVAOMachine.psActiveVAO->ui32DirtyMask)
	{
		if(ValidateState(gc)!=GLES1_NO_ERROR)
		{
			PVR_DPF((PVR_DBG_ERROR,"glMultiDrawElements: ValidateState() failed"));
		
			goto StopTimerAndReturn;
		}
	}

	/*
	 * To avoid spreading loops all over the function   
	 * operations logically unrelated are grouped together in the following 'for' loop:
	 * 1 - check all counts are valid (i.e, greater than or equal to zero)
	 * 2 - carry out total number of indices 
	 * 3 - find out what is the min-max interval of indices
	 */

	for(i = 0; i < (IMG_UINT32)primcount; i++)
	{
		IMG_UINT32 ui32TempMin = 0xFFFFFFFF, ui32TempMax = 0;

		/* 1 */
		if(count[i] < 0)
		{
			goto bad_value;
		}
		else
		{
			if (count[i] == 0)
			{
				continue;
			}
		}

		/* 2 */
		/*  
			this if-else is to cope with an asimmetry:
			- sub-routines for HW supported primitives require adjusted count values,e.g.:
			 2 triangles, primcount = 2, count[0] = 4, count[1] = 5
			 In this case we want actualcount[0] = 3, actualcount[1] = 3, numindices = 9
			- sub-routines for non HW supported primitives (i.e., line strips and loops) use original counts, e.g.:
			 2 lines strips, primcount =2, count[0] = 3, count[1] = 4
			 Then we want: actualcount[0] = 3, actualcount[1] = 4, because the original counts are
			 used by write_line_strips_or_loops, and numindices = 10, as we need more indices 
		*/
		if(bIsHWSupported)
		{
			pui32ActualCount[ui32ActualPrimCount] = GetNumIndices(mode, (IMG_UINT32)count[i]);
			ui32NumIndices += (IMG_UINT32)count[i]; 
		}
		else
		{
			pui32ActualCount[ui32ActualPrimCount] = (IMG_UINT32)count[i];	
		
			    ui32NumIndices += GetNumIndices(mode, (IMG_UINT32)count[i]); 
		}

		/* Record index pointers for primitives with count > 0 */
		if(pui32ActualCount[ui32ActualPrimCount] > 0)
		{
			ppvActualIndices[ui32ActualPrimCount] = indices[i];

			/* 3 */
			DetermineMinAndMaxIndices(gc, pui32ActualCount[ui32ActualPrimCount], type, indices[i], &ui32TempMin, &ui32TempMax);

			if(ui32TempMin < ui32MinIndex)
			{
				ui32MinIndex = ui32TempMin;
			}
			if(ui32TempMax > ui32MaxIndex)
			{
				ui32MaxIndex = ui32TempMax;
			}
			ui32ActualPrimCount++;
		}
	}

	if(ui32NumIndices == 0)
	{
		goto StopTimerAndReturn;
	}

	/* Check whether any buffer object has undefined memory. 
	   This could cause visual corruption or lockup, so return without drawing. 
	   An OUT_OF_MEMORY error will have been raised at the point of allocation failure.
	 */
	if(psVAOMachine->ui32ControlWord & ATTRIBARRAY_BAD_BUFOBJ)
	{
		goto StopTimerAndReturn;
	}

#if defined(GLES1_EXTENSION_MAP_BUFFER)
	/* Check whether any buffer object is mapped */
	if(psVAOMachine->ui32ControlWord & ATTRIBARRAY_MAP_BUFOBJ)
	{
		SetError(gc, GL_INVALID_OPERATION);
		
		goto StopTimerAndReturn;
	}
#endif /* defined(GLES1_EXTENSION_MAP_BUFFER) */


	if (VAO_IS_ZERO(gc)) /* This is for the DEFAULT VAO */
	{
		/* Set vertex range for client attribute arrays */
		if(psVAOMachine->ui32ControlWord & ATTRIBARRAY_SOURCE_VARRAY)
		{
			ui32VertexStart = ui32MinIndex;
			ui32VertexCount = ui32MaxIndex - ui32MinIndex + 1;	/* PRQA S 3382 */ /* The wraparound past zero does not happen here. */

			if(VAO_INDEX_BUFFER_OBJECT(gc) && (GL_UNSIGNED_BYTE != type)) 
			{
				ui32VertexCount += ui32VertexStart;		/* PRQA S 3382 */ /* The wraparound past zero does not happen here. */

				ui32VertexStart = 0;
			}
		}
		else
		{
			/* Because all the vertex data comes from buffer objects (or is copied from current state), there is 
			 * no need to know the vertex range.
			 */
			ui32VertexStart = 0;
			ui32VertexCount = 0;
		}
	}
	else /* The following is for the active NON-ZERO VAO */
	{
		/* Because all the vertex data comes from buffer objects (or is copied from current state), 
		   there is no need to know the vertex range. */
		ui32VertexStart = 0;
		ui32VertexCount = 0;
	}	


	pfnMultiDrawElements = PickMultiDrawElementsProc(gc, mode, type, ui32NumIndices, ui32VertexCount, ui32MaxIndex);

	/* Attach all used resources to the current surface */
	AttachAllUsedResourcesToCurrentSurface(gc);

	/* All 8-bit indices must be promoted to 16-bit */
	if(GL_UNSIGNED_BYTE == type)
	{
		for(i = 0; i < ui32ActualPrimCount; i++)
		{
			ppvElements[i] = TransformIndicesTo16Bits(gc, pui32ActualCount[i], type, ppvActualIndices[i]);
			
			if(!ppvElements[i])
			{
				goto StopTimerAndReturn;
			}
		}	
		type = GL_UNSIGNED_SHORT;		

		/* Call the actual draw element function */
		(*pfnMultiDrawElements)(gc, mode, 0, pui32ActualCount, ui32NumIndices, type, (const IMG_VOID **)ppvElements, ui32VertexStart, ui32VertexCount, ui32ActualPrimCount);
		
		for(i = 0; i < ui32ActualPrimCount; i++)
		{
			GLES1Free(IMG_NULL, (IMG_VOID *)((IMG_UINTPTR_T)(ppvElements[i])));
		}
	}
	else if ((VAO_INDEX_BUFFER_OBJECT(gc)) && (pfnMultiDrawElements != MultiDrawElementsIndexBO))
	{
		/* The given 'indices' pointer is actually an offset when an index buffer object is bound */
		for(i = 0; i < ui32ActualPrimCount; i++)
		{
			PVRSRV_CLIENT_MEM_INFO *psMemInfo = IMG_NULL;
			GLESBufferObject *psIndexBO = psVAOMachine->psBoundElementBuffer;

			/* Setup psMemInfo using the current VAO's bound element buffer object */
			if (psIndexBO)
			{
				psMemInfo = psIndexBO->psMemInfo;
			}
			
			if (psMemInfo)
			{
			    ppvElements[i] = (const IMG_VOID *)((const IMG_UINT8*)psMemInfo->pvLinAddr + GLES1_BUFFER_OFFSET(ppvActualIndices[i]));
			}
		}
	}
	else
	{
		for(i = 0; i < ui32ActualPrimCount; i++)
		{
			ppvElements[i] = (const IMG_VOID *)ppvActualIndices[i];
		}
	}
		

	/* Call the actual draw element function */
	(*pfnMultiDrawElements)(gc, mode, 0, pui32ActualCount, ui32NumIndices, type, (const IMG_VOID **)ppvElements, ui32VertexStart, ui32VertexCount, ui32ActualPrimCount);


	/*
		Update vertex and index buffers committed primitive offset
	*/
	CBUF_UpdateVIBufferCommittedPrimOffsets(gc->apsBuffers, &gc->psRenderSurface->bPrimitivesSinceLastTA, (IMG_VOID *)gc, KickLimit_ScheduleTA);

	GLES1_PROFILE_INCREMENT_DRAWELEMENTS_CALLCOUNT(mode);
	GLES1_PROFILE_INCREMENT_DRAWELEMENTS_VERTEXCOUNT(mode, ui32VertexCount);
	GLES1_PROFILE_ADD_STATE_METRIC;

StopTimerAndReturn:
	PVRSRVUnlockMutex(gc->psRenderSurface->hMutex);

	if(ppvElements)
	{
		GLES1Free(IMG_NULL, (IMG_VOID*) ppvElements);
	}

	if(ppvActualIndices)
	{
		GLES1Free(IMG_NULL, (IMG_VOID*) ppvActualIndices);
	}

	if(pui32ActualCount)
	{
		GLES1Free(IMG_NULL, pui32ActualCount);
	}

	GLES1_TIME_STOP(GLES1_TIMER_ELEMENT_POINTS_TIME+mode);
	GLES1_TIME_STOP(GLES1_TIMES_glMultiDrawElements);

}

#endif /* defined(GLES1_EXTENSION_MULTI_DRAW_ARRAYS) */

/******************************************************************************
 End of file (drawvarray.c)
******************************************************************************/

